home *** CD-ROM | disk | FTP | other *** search
/ APDL Eductation Resources / APDL Eductation Resources.iso / programs / astronomy / skyview / !SkyView / c / SkyView < prev    next >
Encoding:
Text File  |  1993-08-26  |  137.7 KB  |  4,093 lines

  1. /*
  2.  * Title:  SkyView
  3.  * Author: N P Hawkes
  4.  *         (c) 1992
  5.  * Purpose: View the sky from any point on the
  6.  *          Earth's surface, at any time in 20th
  7.  *          or 21st centuries.
  8.  */
  9. #define VERSION "1.02"
  10.  
  11. #include "event.h"
  12. #include "baricon.h"
  13. #include "res.h"
  14. #include "resspr.h"
  15. #include "win.h"
  16. #include "wimpt.h"
  17. #include "menu.h"
  18. #include "template.h"
  19. #include "dbox.h"
  20. #include "bbc.h"
  21. #include "os.h"
  22. #include "coords.h"
  23. #include "colourtran.h"
  24. #include "print.h"
  25. #include "werr.h"
  26. #include "visdelay.h"
  27. #include "sprite.h"
  28. #include "signal.h"
  29. #include "font.h"
  30. #include "kernel.h"
  31.  
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <stdlib.h>
  35. #include <math.h>
  36. #include <time.h>
  37.  
  38. #include "sv_header.h"
  39. #include "datime.h"
  40. #include "tvsats.h"
  41. #include "markers.h"
  42. #include "stars.h"
  43. #include "sun.h"
  44. #include "planets.h"
  45. #include "moon.h"
  46. #include "userobj.h"
  47.  
  48. /********************************************************/
  49. /*                       CONSTANTS                      */
  50. /********************************************************/
  51.  
  52. /*------------------------------------------------------*/
  53. /*                         Names                        */
  54. /*------------------------------------------------------*/
  55.  
  56. #define APP_NAME      "SkyView"    /* Task name       */
  57. #define ICON_NAME     "!skyview"   /* Icon Bar icon   */
  58. #define HORIZ_NAME    "Horiz"      /* Horiz window id */
  59. #define VERT_NAME     "Vert"       /* Vert window id  */
  60. #define OB_NAME       "Observer"   /* Observer dbox   */
  61. #define INFO_NAME     "Info"       /* Info dbox       */
  62. #define DEF_NAME      "ObData"     /* File of defaults*/
  63. #define SEL_INFO_NAME "SelInfo"    /* Sel Info dbox   */
  64.  
  65. /*------------------------------------------------------*/
  66. /*                        Numbers                       */
  67. /*------------------------------------------------------*/
  68.  
  69. #define DIR_BASE 1      /* Icon No. of North button in  */
  70.                         /* Observer Details dbox        */
  71. #define NO_ENTRY -1     /* Dummy entry number for a menu*/
  72.                         /* entry which does not exist.  */
  73.  
  74. /*------------------------------------------------------*/
  75. /*                   Main Menu Items                    */
  76. /*------------------------------------------------------*/
  77.  
  78. #define MAIN_NAME APP_NAME
  79.  
  80. #define MAIN_MENU_ITEMS "\
  81.  >Program info,     \
  82.   Observer...,      \
  83.  ~Display,          \
  84.  ~Horizontal view,  \
  85.  ~Vertical view,    \
  86.   Print|            \
  87.  ~Select object,    \
  88.  ~See selection,    \
  89.  ~Selection info,   \
  90.  ~Cancel selection| \
  91.   Quit"
  92.  
  93. enum {item_info = 1,
  94.       item_observer,
  95.       item_display,
  96.       item_horiz,
  97.       item_vert,
  98.       item_print,
  99.       item_select,
  100.       item_view,
  101.       item_details,
  102.       item_cancel,
  103.       item_quit      };
  104.  
  105.  
  106. /*------------------------------------------------------*/
  107. /*                "View" Sub-menu Items                 */
  108. /*------------------------------------------------------*/
  109.  
  110. #define VIEW_SUB_NAME "See selectn:"
  111.  
  112. #define VIEW_SUB_MENU_ITEMS "\
  113.   Now,          \
  114.   On rising,    \
  115.   On setting,   \
  116.   On culminating"
  117.  
  118. enum {view_item_now = 1,
  119.       view_item_rising,
  120.       view_item_setting,
  121.       view_item_culminating};
  122.  
  123. /*------------------------------------------------------*/
  124. /*               "Print" Sub-menu Items                 */
  125. /*------------------------------------------------------*/
  126.  
  127. #define PRINT_SUB_NAME "Print:"
  128.  
  129. #define PRINT_SUB_MENU_ITEMS "\
  130.   Horiz. quadrant,\
  131.   Vert. complete"
  132.  
  133. enum {print_item_horizq = 1,
  134.       print_item_vertc};
  135.  
  136. /*------------------------------------------------------*/
  137. /*                  Other Sub-Menu Items                */
  138. /*------------------------------------------------------*/
  139.  
  140. #define  SEL_SUB_NAME  "Select:"
  141. #define DISP_SUB_NAME  "Display:"
  142.  
  143. /*------------------------------------------------------*/
  144. /*                  Miscellaneous Items                 */
  145. /*------------------------------------------------------*/
  146.  
  147. #define sv_info_field     4  /* Field for version No.   */
  148.  
  149.  
  150. /********************************************************/
  151. /*                  New types of variable               */
  152. /********************************************************/
  153.  
  154. /*------------------------------------------------------*/
  155. /*   Structures and Function Types for the windows      */
  156. /*------------------------------------------------------*/
  157.  
  158. /* Set up a structure similar to a wimp_box, but        */
  159. /* designed to suit the arguments of bbc_rectangle.     */
  160. typedef struct {
  161.   int x;         /* x      */
  162.   int y;         /* y      */
  163.   int w;         /* width  */
  164.   int h;         /* height */
  165. } rect_box;
  166.  
  167. /* Define a type for a compass-drawing function, which  */
  168. /* takes no arguments and returns an os_error pointer:  */
  169. typedef os_error *comp_drawfntype(void);
  170.  
  171. /* Define a type for a title-building function, which   */
  172. /* takes a char pointer and a BOOL as argument and      */
  173. /* returns void:                                        */
  174. typedef void title_buildfntype(char *title, BOOL font_ok);
  175.  
  176. /* Define a type for a title-writing function, which    */
  177. /* takes a char pointer and a flag and returns an       */
  178. /* os_error pointer:                                    */
  179. typedef os_error *title_writefntype(char *title, BOOL font_ok);
  180.  
  181. /* Structure containing information to particularise    */
  182. /* the generic window-handling functions.               */
  183. typedef struct {
  184.   BOOL open;                      /* Open/Closed flag.  */
  185.   comp_drawfntype *compass_drawfn;/*Compass-drawing fn. */
  186.   plotobj* listptr;          /* Pointer to plotting list*/
  187.   int number;                /* No. of objects in list. */
  188.   int selnum;                /* ID of selected object.  */
  189.   rect_box sel_frame;        /* Frame round selected obj*/
  190.   wimp_box sel_bounds;       /* Bounding box of frame   */
  191.                              /* around selected object. */
  192.   wimp_box wind_area;        /* Area to be printed.     */
  193.   print_transmatstr matrix;  /* Transfm matrix for print*/
  194.   print_positionstr posn;    /* Position for print o/p. */
  195.   title_buildfntype *title_buildfn; /*Title-building fn.*/
  196.   title_writefntype *title_writefn; /*Title-writing  fn.*/
  197. } sv_windata;
  198.  
  199. /*------------------------------------------------------*/
  200. /*             Structure for Compass Label              */
  201. /*------------------------------------------------------*/
  202.  
  203. typedef struct {
  204.   int  len;          /* Length of compass label - 1     */
  205.   char name[3];      /* Character string for label      */
  206. } c_label;
  207.  
  208. /*------------------------------------------------------*/
  209. /*       Structure for recording Module functions       */
  210. /*------------------------------------------------------*/
  211.  
  212. typedef struct {
  213.   buildfntype   *buildfn;  /* Ptr to List Building fn.  */
  214.   selectfntype  *selectfn; /* Ptr to Obj Selecting fn.  */
  215.   displayfntype *dispfn;   /* Ptr to Disp Option fn.    */
  216.   infofntype    *infofn;   /* Ptr to Info function.     */
  217. } fnliststr;
  218.  
  219. /*------------------------------------------------------*/
  220. /* Structure for converting argument of Wimp_SetColour  */
  221. /* into separate colour, gcol action and fore/back flag.*/
  222. /*------------------------------------------------------*/
  223.  
  224. typedef union {
  225.   int argument;
  226.   struct {unsigned int w_colour :4;
  227.           unsigned int gcol_act :3;
  228.           unsigned int foreback :1;
  229.           } fields;
  230.   } ctrans_type;
  231.  
  232. /********************************************************/
  233. /*                FUNCTION PROTOTYPES                   */
  234. /********************************************************/
  235.  
  236. static void sv_info_about_program(void);
  237. static void main_menuproc(void *handle, char *hit);
  238. static menu main_premenuproc(void *handle);
  239. static void view_sub_menuproc(char *hit);
  240. static BOOL sv_initialise(void);
  241. static BOOL create_window(char *name, wimp_w *handle, wimp_box *size);
  242. static void sv_win_event_handler(wimp_eventstr *e, void *handle);
  243. static void    redraw_window(wimp_w handle, sv_windata *wd);
  244. static os_error *draw_window(sv_windata *wd, wimp_box to_do);
  245. static void toggle_window(wimp_w handle, BOOL *open);
  246. static os_error *draw_horiz_compass(void);
  247. static os_error *draw_vert_compass(void);
  248. static int  x_h(REAL azimuth);
  249. static int  y_h(REAL altitude);
  250. static void xy_v(REAL altitude, REAL azimuth, int *xptr, int *yptr);
  251. static void observer(void);
  252. static BOOL ob_raw_handler(dbox d, void *event, void *handle);
  253. static void data_to_box(observerstr data);
  254. static void data_from_box(observerstr *ptr);
  255. static void ob_ok_click(void);
  256. static BOOL save_ob_data(void);
  257. static BOOL load_ob_data(void);
  258. static void ob_arrows(int response, int *targptr);
  259. static void load_sys_date(void);
  260. static void sv_Draw_circle(int radius);
  261. static void sv_Draw_move(int x, int y);
  262. static void sv_Draw_line(int x, int y);
  263. static void sv_Draw_bezi(int x1, int y1, int x2, int y2, int x3, int y3);
  264. static void sv_Draw_endp(void);
  265. static BOOL register_module(initfntype *initfn);
  266. static BOOL install_select_entry(modulestr new_module);
  267. static BOOL install_display_entry(modulestr new_module);
  268. static void invoke_displayfns(char *hit);
  269. static void display_rebuild(void);
  270. static void recalc_horiz_xy(void);
  271. static void recalc_vert_xy(void);
  272. static void window_click(int x, int y, wimp_w wh, sv_windata *wd);
  273. static void cutup(char *linestart[]);
  274. static void infobox_load(int objno);
  275. static void infobox_buttons(int nobjects);
  276. static void build_lists(void);
  277. static void invoke_selectfns(char *hit);
  278. static void fillin_obfields(observerstr *ptr);
  279. static void invalidate_framearea(wimp_w whandle, sv_windata wdat);
  280. static void establish_new_frame(wimp_w whandle, sv_windata *wd);
  281. static void calculate_frame(sv_windata *wd);
  282. static void establish_new_selection(int new_owner);
  283. static void invalidate_all(void);
  284. static void cancel_selection(void);
  285. static void sel_info(void);
  286. static void build_timestring(char *stringptr, BOOL valid, int hour, int min);
  287. static int  nearest_dir(REAL azim);
  288. static void arrange_windows(REAL alt, REAL az);
  289. static void arrange_horiz(REAL alt, REAL az);
  290. static void arrange_vert(REAL alt, REAL az);
  291. static void print_sub_menuproc(char *hit);
  292. static BOOL pdriver_installed(void);
  293. static void print_hquadrant(void);
  294. static void print_vertical(void);
  295. static BOOL overlap(wimp_box *b1, wimp_box *b2, wimp_box *olap);
  296. static void build_horiz_title(char *title, BOOL font_ok);
  297. static void build_vert_title(char *title, BOOL font_ok);
  298. static os_error *write_horiz_title(char *title, BOOL font_ok);
  299. static os_error *write_vert_title(char *title,  BOOL font_ok);
  300. static os_error *write_title(int x, int y, char *title, BOOL font_ok);
  301. static void ob_info_string(char *title, BOOL split, BOOL font_ok);
  302. static os_error *write_string(char *title, int *countptr);
  303. static void print_window(sv_windata *wd, int title_ht);
  304. static os_error *sprite_tables(void);
  305. static os_error *save_error(os_error *ptr1, os_error *ptr2);
  306.  
  307.  
  308. /********************************************************/
  309. /*                  GLOBAL VARIABLES                    */
  310. /********************************************************/
  311.  
  312. /*------------------------------------------------------*/
  313. /*                  Program environment                 */
  314. /*------------------------------------------------------*/
  315.        char app_dir[256];        /* For SkyView$Dir.    */
  316. static char start_file[256]="";  /* For cmd line file.  */
  317. static char data_file[256];      /* For name of file of */
  318.                                  /* observer defaults.  */
  319.  
  320. /*------------------------------------------------------*/
  321. /*            Information on the Modules                */
  322. /*------------------------------------------------------*/
  323. static int module_count = 0; /* No. of Modules          */
  324.                              /* registered.             */ 
  325. static int select_count = 0; /* No. of non-null object- */
  326.                              /* selecting fns registered*/
  327. static int display_count = 0;/* No. of non-null display */
  328.                              /* functions (fns to set   */
  329.                              /* display options)        */
  330.                              /* registered.             */
  331. #define MAXMODULES 32
  332.  
  333. /* Set up an array to record which modules are enabled  */
  334. /* and which disabled.                                  */
  335. static BOOL enabled[MAXMODULES];
  336.  
  337. /* Set up an array of fnliststrs, to record a list of   */
  338. /* functions for each module.                           */
  339. static fnliststr module_fns[MAXMODULES];
  340.  
  341. /* Set up an index array which defines which module     */
  342. /* owns the nth entry in the Select menu.               */
  343. static int sel_index[MAXMODULES];
  344.  
  345. /* Set up an index array which defines which module     */
  346. /* owns the nth entry in the Display menu.              */
  347. static int disp_index[MAXMODULES];
  348.  
  349. /* Set up an index array which defines which entry in   */
  350. /* the Select menu is owned by module n.                */
  351. static int module_index[MAXMODULES];
  352.  
  353. /*------------------------------------------------------*/
  354. /* Character arrays etc for receiving info from Modules */
  355. /*------------------------------------------------------*/
  356. #define MAXRQ 5 /*Max no. of simultaneous info requests.*/
  357. static char infobuf[MAXRQ*MAXINFO];
  358. char *infoptr = infobuf;
  359.  
  360. /*------------------------------------------------------*/
  361. /*                   Menu Variables                     */
  362. /*------------------------------------------------------*/
  363. static menu main_menu;       /* Top of menu tree        */
  364. static menu display_sub_menu;/* Sub-menu for 'Display'  */
  365. static menu sel_sub_menu;    /* Sub-menu for 'Sel obj.' */
  366. static menu view_sub_menu;   /* Sub-menu for 'View sel.'*/
  367. static menu print_sub_menu;  /* Sub-menu for 'Print'    */
  368.  
  369. char *module_submenu_hits = NULL; /* For hits on        */
  370.                                   /* module sub-menus.  */
  371.  
  372. /*------------------------------------------------------*/
  373. /* Flag causing Select or Disp fns to be called repeat- */
  374. /* edly.  Used when module has a dbox which is per-     */
  375. /* sisting (user has clicked OK with Adjust).           */
  376. /*------------------------------------------------------*/
  377.   BOOL dbox_persisting = FALSE;
  378.  
  379. /*------------------------------------------------------*/
  380. /* Flag causing plotting lists to be rebuilt following  */
  381. /* a call to a Display function.  Set by the module in  */
  382. /* question if the Display options have been so drastic-*/
  383. /* ally modified (eg change of position) that a simple  */
  384. /* re-drawing of the windows will not do.               */
  385. /*------------------------------------------------------*/
  386.   BOOL rebuild_request = FALSE;
  387.  
  388. /*------------------------------------------------------*/
  389. /*                   Window Variables                   */
  390. /*------------------------------------------------------*/
  391. static wimp_w horiz_handle;  /* Handle for Horiz window */
  392. static sv_windata horiz_data;/* Data for Horiz window   */
  393. static wimp_box horiz_size;  /* Dimens. of Horiz window */
  394.  
  395. static wimp_w vert_handle;   /* Handle for Vert window  */
  396. static sv_windata vert_data; /* Data for Vert window    */
  397. static wimp_box vert_size;   /* Dimens. of Vert window  */
  398.  
  399. /*------------------------------------------------------*/
  400. /*                     Screen Data                      */
  401. /*------------------------------------------------------*/
  402. static int  width;           /* Width of horiz window,  */
  403.                              /* OS units.               */
  404. static int  height;          /* Height of horiz window. */
  405. static REAL factor;          /* Scaling factor in plott-*/
  406.                              /* ing functions.          */
  407. static REAL compass_alt \
  408.  =(REAL)59.5 * CONV;         /* 'Altitude' of compass   */
  409.                              /* circle in Vert window.  */
  410. static REAL alt_max;         /* Maximum altitude        */
  411.                              /* in Horiz window.        */
  412. static REAL alt_min;         /* Min altitude            */
  413.                              /* in Vert window.         */
  414. static wimp_box targ = \
  415.          {-8, -8, 8, 8};     /* Target for mouse clicks */
  416.                              /* on objects in windows.  */
  417.  
  418. /*------------------------------------------------------*/
  419. /*              Palette and Printing Data               */
  420. /*------------------------------------------------------*/
  421. static BOOL printing = FALSE; /* TRUE if currently      */
  422.                               /* printing (via driver). */
  423.  
  424. /* Set up a palette describing the standard Wimp colours*/
  425. static wimp_paletteword screen_palette[16] =
  426.   {
  427.     {0x00, 0xFF, 0xFF, 0xFF},
  428.     {0x00, 0xDD, 0xDD, 0xDD},
  429.     {0x00, 0xBB, 0xBB, 0xBB},
  430.     {0x00, 0x99, 0x99, 0x99},
  431.     {0x00, 0x77, 0x77, 0x77},
  432.     {0x00, 0x55, 0x55, 0x55},
  433.     {0x00, 0x33, 0x33, 0x33},
  434.     {0x00, 0x00, 0x00, 0x00},
  435.     {0x00, 0x00, 0x44, 0x99},
  436.     {0x00, 0xEE, 0xEE, 0x00},
  437.     {0x00, 0x00, 0xCC, 0x00},
  438.     {0x00, 0xDD, 0x00, 0x00},
  439.     {0x00, 0xEE, 0xEE, 0xBB},
  440.     {0x00, 0x55, 0x88, 0x00},
  441.     {0x00, 0xFF, 0xBB, 0x00},
  442.     {0x00, 0x00, 0xBB, 0xFF}
  443.   };
  444.  
  445. /* Set up a palette which is the same as the standard   */
  446. /* Wimp palette, but which has an inverted grey scale.  */
  447. static wimp_paletteword printer_palette[16] =
  448.   {
  449.     {0x00, 0x00, 0x00, 0x00},
  450.     {0x00, 0x33, 0x33, 0x33},
  451.     {0x00, 0x55, 0x55, 0x55},
  452.     {0x00, 0x77, 0x77, 0x77},
  453.     {0x00, 0x99, 0x99, 0x99},
  454.     {0x00, 0xBB, 0xBB, 0xBB},
  455.     {0x00, 0xDD, 0xDD, 0xDD},
  456.     {0x00, 0xFF, 0xFF, 0xFF},
  457.     {0x00, 0x00, 0x44, 0x99},
  458.     {0x00, 0xEE, 0xEE, 0x00},
  459.     {0x00, 0x00, 0xCC, 0x00},
  460.     {0x00, 0xDD, 0x00, 0x00},
  461.     {0x00, 0xEE, 0xEE, 0xBB},
  462.     {0x00, 0x55, 0x88, 0x00},
  463.     {0x00, 0xFF, 0xBB, 0x00},
  464.     {0x00, 0x00, 0xBB, 0xFF}
  465.   };
  466.  
  467. /* Declare pixel translation tables for sprite plotting.*/
  468. static sprite_pixtrans sc_pixtrans[256]; /* For screen. */
  469. static sprite_pixtrans pr_pixtrans[256]; /* For printer.*/
  470.  
  471. /* Declare 'sprite factor' tables for sprite plotting.  */
  472. static sprite_factors s_factors20; /*For mode 20 sprites*/
  473. static sprite_factors s_factors12; /*For mode 12 sprites*/
  474.  
  475. /* Define some constants for functions which build title*/
  476. #define TITLE_LEN 256
  477. #define THREE_LINES TRUE
  478. #define ONE_LINE    FALSE
  479.  
  480. /* Define overall scaling factor for printing.          */
  481. #define SCALFACT (5<<14)
  482.  
  483. /* Define some constants for control of Font used to    */
  484. /* print title strings.                                 */
  485. #define TITLE_FONT "Homerton.Medium"
  486. #define PAL_BLACK 0x00000000
  487. #define PAL_WHITE 0xFFFFFF00
  488. #define FONT_SIZE ((1<<10)/5)
  489. #define VSHIFT 28
  490.  
  491. /*------------------------------------------------------*/
  492. /*           Plotting Lists for Windows                 */
  493. /*------------------------------------------------------*/
  494. #define MAXOBJS 600
  495. static plotobj horiz_list[MAXOBJS];
  496. static plotobj vert_list[MAXOBJS];
  497.  
  498. /*------------------------------------------------------*/
  499. /*               Compass-Drawing Data                   */
  500. /*------------------------------------------------------*/
  501. #define NDIR 8     /* No. of directions of view offered */
  502.                    /* to user, and number of compass    */
  503.                    /* labels in Horiz and Vert windows. */
  504.  
  505. /* Array giving the NDIR azimuthal offsets corresponding*/
  506. /* to the "directions of view" offered to the user, and */
  507. /* (equivalently) the positions of the compass labels.  */
  508.  
  509. static REAL direction[] = {
  510.  (REAL)(  0.0)*CONV,
  511.  (REAL)( 45.0)*CONV,
  512.  (REAL)( 90.0)*CONV,
  513.  (REAL)(135.0)*CONV,
  514.  (REAL)(180.0)*CONV,
  515.  (REAL)(225.0)*CONV,
  516.  (REAL)(270.0)*CONV,
  517.  (REAL)(315.0)*CONV};
  518.  
  519.  
  520. /* Array giving the NDIR compass labels to be used by   */
  521. /* the compass-drawing functions.                       */
  522.  
  523. static c_label compass[] = {
  524.   {0, "N" },
  525.   {1, "NE"},
  526.   {0, "E" },
  527.   {1, "SE"},
  528.   {0, "S" },
  529.   {1, "SW"},
  530.   {0, "W" },
  531.   {1, "NW"}
  532. };
  533.  
  534.  
  535. /*------------------------------------------------------*/
  536. /* Variables & constants for invoking Draw Module in    */
  537. /* Vert window.                                         */
  538. /*------------------------------------------------------*/
  539.  
  540. #define sv_XDraw_ProcessPath 0x60700
  541. #define sv_XDraw_StrokePath  0x60706
  542. #define sv_XDraw_Stroke      0x60704
  543. #define DrawU    100
  544. #define PathMax  254
  545. #define OutPMax 2048
  546.  
  547. /* Set up Draw's transformation matrix so that 1 'user  */
  548. /* unit' is 1/DrawU OS unit.                            */
  549. /* For now, assume DrawU = 100                          */
  550. static int matrix[6] = {0x28F5C, 0, 0, 0x28F5C, 0, 0};
  551.  
  552. /* Set butt caps and round joins.                       */
  553. static int cj_style  = 1;
  554.  
  555. /* Reserve space for Draw Paths.  Set up path pointer.  */
  556. static int path[PathMax+2];
  557. static int pathptr = 0;
  558. static int semi[OutPMax]= {0,OutPMax*4 - 8};
  559.                           /* Fill in No. of free bytes. */
  560.  
  561. /* 'Register Set' structures for use with Draw SWIs.    */
  562. static os_regset draw_regsS;
  563. static os_regset draw_regsP;
  564.  
  565.  
  566. /*------------------------------------------------------*/
  567. /*         Variables relating to Selected Object        */
  568. /*------------------------------------------------------*/
  569.  
  570.        selectstr selection;
  571. static BOOL selection_exists = FALSE;
  572. static int sel_owner; /* Module number of current owner */
  573.                       /* of selection.                  */
  574.  
  575. /*------------------------------------------------------*/
  576. /*       Variables relating to Observer Details         */
  577. /*------------------------------------------------------*/
  578.  
  579. static dbox d_ob;         /* dbox for Observer Details. */
  580. static wimp_w ob_handle;  /* handle for Observer Box.   */
  581.  
  582. static BOOL observer_verified = FALSE;  /* Flag denoting*/
  583.      /* whether user has verified the observer details. */
  584.  
  585. /* Set 'factory defaults' for Observer Details          */
  586.        observerstr ob_data = {
  587.  517,                /* Latitude *10 */
  588.  TRUE,               /* North        */
  589.  (REAL)0.902335,     /* Latitude     */
  590.  13,                 /* Longitude*10 */
  591.  TRUE,               /* West         */
  592.  (REAL)0.0226893,    /* Longitude    */
  593.  1,                  /* Day          */
  594.  6,                  /* Month        */
  595.  1992,               /* Year         */
  596.  0,                  /* Hour         */
  597.  0,                  /* Minutes      */
  598.  0.0f,               /* Offset       */
  599.  33754.5,            /* Julian       */
  600.  (REAL)4.33582,      /* Loc Siderial */
  601.  0,                  /* View North   */
  602.  (REAL)0.0,          /* Radians      */
  603. };
  604.  
  605. /* Set up named constants for the field numbers in the  */
  606. /* Observer Details dbox.                               */
  607. enum {
  608.   ob_close_rq    =-1,    /* Result of cancelling dbox.  */
  609.   ob_ok_butt     = 0,    /* Icon No. of OK button.      */
  610.   ob_cancel_butt = 9,    /* Icon No. of Cancel button.  */
  611.   set_butt       =10,    /* Icon No. of Set  button.    */
  612.   sys_date_butt  =12,    /* Icon No. of Sys Time button.*/
  613.   show_butt      =70,    /* Icon No. of Show button.    */
  614.   cent_butt = 30,        /* Icon No. of century button. */
  615.   e_or_w    = 55,        /* Icon No. of E or W display. */
  616.   ew_butt   = 56,        /* Icon No. of E/W button.     */
  617.   n_or_s    = 66,        /* Icon No. of N or S display. */
  618.   ns_butt   = 67,        /* Icon No. of N/S button.     */
  619.   dayf = 13, day_tu=14, day_td, day_uu, day_ud, /* Day    */
  620.   monf = 19, mon_tu=21, mon_td, mon_uu, mon_ud, /* Month  */
  621.   yrf  = 25,  yr_tu=26,  yr_td,  yr_uu,  yr_ud, /* Year   */
  622.   hrf  = 31,  hr_tu=32,  hr_td,  hr_uu,  hr_ud, /* Hour   */
  623.   minf = 37, min_tu=38, min_td, min_uu, min_ud, /* Minute */
  624.   /* Longitude, Integer Part: */
  625.   loif = 44, loi_hu=47, loi_hd, loi_tu, loi_td, loi_uu, loi_ud,
  626.   /* Longitude, Decimal Part: */
  627.   lodf = 46, lod_uu=53, lod_ud,
  628.   ewf  = 55,       /* E or W */
  629.   /* Latitude, Integer Part: */
  630.   laif = 57, lai_tu=60, lai_td, lai_uu, lai_ud,
  631.   /* Latitude, Decimal Part: */
  632.   ladf = 59, lad_uu=64, lad_ud,
  633.   nsf  = 66,       /* N or S */
  634.   /* Time offset:            */
  635.   offf = 74, off_uu=75, off_ud, off_xu, off_xd};
  636.  
  637. /* Increments corresponding to the arrow buttons:       */
  638.   static int addon[6] = {-1, 1, -10, 10, -100, 100};
  639.  
  640. /* Special increments for Time Offset field:            */
  641.   static float offset_incr[4] = {-0.5f, 0.5f, -1.0f, 1.0f};
  642.  
  643. /* Days in each month, for non-leap and leap years:     */
  644.   static int daysin[2][13] = {
  645.     {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  646.     {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  647.   };
  648.  
  649. /*------------------------------------------------------*/
  650. /*      Variables & Constants relating to Info Box      */
  651. /*------------------------------------------------------*/
  652. static dbox d_info;
  653. enum {
  654.   info_cancel  =-1,   /* Response cancelling the dbox.  */
  655.   info_ok      = 0,   /* OK button.                     */
  656.   info_select,        /* Select button.                 */
  657.   alt_field    = 4,   /* Altitude of object.            */
  658.   azim_field,         /* Azimuth of object.             */
  659.   info_uparrow,       /* Butt. to display next object.  */
  660.   info_objno,         /* Display of object's number.    */
  661.   info_dnarrow,       /* Butt. to display prev object.  */
  662.   info_textline       /* First line of text.            */
  663. };
  664.  
  665. /* For info on objects which have been clicked on.      */
  666. static int  info_id[MAXRQ];
  667. static int  info_modno[MAXRQ];
  668. static selectfntype *sel_fn_ptr[MAXRQ];
  669. static char *info_lineptr[MAXRQ][INFOBOX_LINES];
  670. static REAL info_alt[MAXRQ];
  671. static REAL info_azim[MAXRQ];
  672.  
  673. /*------------------------------------------------------*/
  674. /*       Constants realting to Selection Info Box       */
  675. /*------------------------------------------------------*/
  676. enum {
  677.   selinfo_cancel = -1, /* Response cancelling the dbox. */
  678.   selinfo_ok,          /* OK button.                    */
  679.   selinfo_rise,        /* Field for time of rising.     */
  680.   selinfo_set,         /* Field for time of setting.    */
  681.   selinfo_culmin,      /* Field for time of culminating.*/
  682.   selinfo_textline     /* First line of text.           */
  683. };
  684.  
  685.  
  686. /********************************************************/
  687. /*           Generic Window-Handling Functions          */
  688. /********************************************************/
  689.  
  690. /*------------------------------------------------------*/
  691. /* Create a window, yielding its handle.                */
  692. /* Return TRUE if OK                                    */
  693. /*------------------------------------------------------*/
  694.  
  695. static BOOL create_window(char *name, wimp_w *handle, wimp_box *size)
  696. {
  697.   wimp_wind *window;    /* pointer to window definition */
  698.   os_error *creation_error;
  699.  
  700.   window = template_syshandle(name);  /* find template  */
  701.   if (window == 0)
  702.     return FALSE;
  703.  
  704.   /* create window, handling any errors                 */
  705.   creation_error=wimpt_complain(wimp_create_wind(window, handle));
  706.   if (creation_error != 0)
  707.     return FALSE;
  708.  
  709.   /* Make a note of the dimensions of the window.       */
  710.   *size = window->ex;
  711.  
  712.   return TRUE;
  713. }
  714.  
  715. /*------------------------------------------------------*/
  716. /*             Event Handler for a window               */
  717. /*------------------------------------------------------*/
  718.  
  719. static void sv_win_event_handler(wimp_eventstr *e, void *handle)
  720. /* handle points to a data block for the window */
  721. {
  722.   sv_windata *windata = handle;
  723.  
  724.   switch (e->e)
  725.   {
  726.     case wimp_EREDRAW:
  727.       /* Register current screen mode.  Re-calculate    */
  728.       /* sprite-plotting tables if mode has changed.    */
  729.       if (wimpt_checkmode()) wimpt_noerr(sprite_tables());
  730.       /* Draw requested part of requested window.       */
  731.       redraw_window(e->data.o.w, windata);
  732.       break;
  733.  
  734.     case wimp_EOPEN:
  735.       wimpt_noerr(wimp_open_wind(&e->data.o));
  736.       break;
  737.  
  738.     case wimp_ECLOSE:
  739.       wimpt_noerr(wimp_close_wind(e->data.o.w));
  740.       windata->open = FALSE;
  741.       break;
  742.  
  743.     case wimp_EBUT:
  744.     {
  745.       int x = e->data.but.m.x;    /* Coordinates of     */
  746.       int y = e->data.but.m.y;    /* mouse click.       */
  747.       wimp_w wh = e->data.but.m.w;/* Window handle.     */
  748.       window_click(x, y, wh, windata);
  749.       break;
  750.     }
  751.  
  752.     default:
  753.       break;
  754.   }
  755.   return;
  756. }
  757.  
  758. /*------------------------------------------------------*/
  759. /*        Handler for mouse click inside window         */
  760. /*------------------------------------------------------*/
  761. static void window_click(int x, int y, wimp_w wh, sv_windata *wd)
  762. {
  763.   wimp_wstate result;           /*Data on window.       */
  764.   coords_cvtstr conv;           /*For doing conversions.*/
  765.   coords_pointstr pt;           /*For mouse coordinates.*/
  766.   wimp_box work_targ;           /*Target for clicks, in */
  767.                                 /*work area coords.     */
  768.   infofntype *infofn;           /*Ptr to info function. */
  769.  
  770.   plotobj *pstart=wd->listptr;  /*Point to start of list*/
  771.   plotobj *pend  =pstart + wd->number; /* Point to end. */
  772.   plotobj *p;
  773.  
  774.   int hits = 0;
  775.  
  776. /* Obtain data to convert between screen coords and     */
  777. /* window workspace coords.                             */
  778.   wimpt_noerr(wimp_get_wind_state(wh, &result));
  779.  
  780. /* Fill in the coords_cvtstr required by conversion     */
  781. /* functions.                                           */
  782.   conv.box = result.o.box;
  783.   conv.scx = result.o.x;
  784.   conv.scy = result.o.y;
  785.  
  786. /* Fill in the coords_pointstr with the screen coords of*/
  787. /* the mouse click.                                     */
  788.   pt.x = x;
  789.   pt.y = y;
  790.  
  791. /* Convert to work area coords.                         */
  792.   coords_point_toworkarea(&pt, &conv);
  793.  
  794. /* Search for hits with mouse on plotting objects.      */
  795. /* Ignore objects whose parent module is currently      */
  796. /* disabled, or whose parent module has no info fn.     */
  797. /* Max No. of 'hits' is MAXRQ.                          */
  798.  
  799.   for(p = pstart; p<pend && hits<MAXRQ; p++)
  800.   {
  801.     /* Set up target for this object.                   */
  802.     coords_offsetbox(&targ, p->wind_x, p->wind_y, &work_targ);
  803.     /* Test for a hit.                                  */
  804.     if (coords_withinbox(&pt, &work_targ) &&\
  805.         enabled[p->module]                &&\
  806.         (infofn = module_fns[p->module].infofn, infofn != NULL))
  807.     {
  808.  
  809.       /* Point to place to write info string.           */
  810.       infoptr = infobuf + hits*MAXINFO;
  811.  
  812.       /* Get info from module.                          */
  813.       infofn(p->id);
  814.  
  815.       /* Cut info string up into INFOBOX_LINES lines.   */
  816.       cutup(info_lineptr[hits]);
  817.  
  818.       /* Store data on object.  Increment hits at end.  */
  819.       sel_fn_ptr[hits] = \
  820.        module_fns[p->module].selectfn; /* Ptr to sel fn.*/
  821.       info_modno[hits]= p->module;/* Module number.     */
  822.       info_id[hits]   = p->id;    /* Object's id.       */
  823.       info_alt[hits]  = p->alt;   /* Altitude.          */
  824.       info_azim[hits] = p->azim;  /* Azimuth.           */
  825.       hits++;
  826.     }
  827.   }
  828.  
  829.   if (hits==0)  return;
  830.  
  831. /* Load info dbox with details of last object.          */
  832.   infobox_load(hits-1);
  833.  
  834. /* Fade out up and down arrows if there is only 1 object*/
  835.   if (hits==1)
  836.   {
  837.     dbox_fadefield(d_info, info_uparrow);
  838.     dbox_fadefield(d_info, info_dnarrow);
  839.   }
  840.   else
  841.   {
  842.     dbox_unfadefield(d_info, info_uparrow);
  843.     dbox_unfadefield(d_info, info_dnarrow);
  844.   }
  845.  
  846.  
  847. /* Display dbox.                                        */
  848.   dbox_show(d_info);
  849.  
  850. /* Deal with clicks on its buttons, changing between    */
  851. /* 'hits' different objects as requested by user.       */
  852.   infobox_buttons(hits);
  853.  
  854. /* Remove dbox from screen.                             */
  855.   dbox_hide(d_info);
  856.  
  857.   return;
  858. }
  859.  
  860.  
  861. static void cutup(char *linestart[])
  862. /* Divide the contents of the info buffer into          */
  863. /* INFOBOX_LINES separate lines.                        */
  864. /* Write pointers to the start of each line into the    */
  865. /* pointer array linestart.                             */
  866. {
  867.   int i;
  868.   char *startptr;
  869.  
  870.   /* Make sure there is an end-of-string marker in the  */
  871.   /* buffer.                                            */
  872.   *(infoptr+MAXINFO-1) = '\0';
  873.  
  874.   /* First line always starts at start of buffer.       */
  875.   linestart[0] = infoptr;
  876.  
  877.   /* Look for the remaining INFOBOX_LINES - 1 lines.    */
  878.   for (i=1; i<INFOBOX_LINES; i++)
  879.   {
  880.     /* Look for end-of-line marker.                     */
  881.     startptr = strchr(linestart[i-1],'\n');
  882.     if (startptr != NULL)
  883.       /* If found, change it to end-of-string, and point*/
  884.       /* to start of next line.                         */
  885.       *startptr++ = '\0';
  886.     else
  887.       /* If not found, point to end-of-string marker.   */
  888.       startptr = linestart[i-1] + strlen(linestart[i-1]);
  889.     linestart[i] = startptr;
  890.   }
  891.  
  892.   return;
  893. }
  894.  
  895.  
  896. static void infobox_load(int objno)
  897. {
  898. /* Load details of object objno into info dbox.         */
  899.  
  900.   int i;
  901.   REAL degrees;
  902.   char numbuf[32];
  903.  
  904.   /* Fade Select button if object cannot be selected.   */
  905.   if (sel_fn_ptr[objno]==NULL)
  906.     dbox_fadefield(d_info, info_select);
  907.   else
  908.     dbox_unfadefield(d_info, info_select);
  909.  
  910.   /* Altitude field.                                    */
  911.   degrees = info_alt[objno]/CONV;
  912.   sprintf(numbuf, "%+5.1f°", (double)degrees);
  913.   dbox_setfield(d_info, alt_field, numbuf);
  914.  
  915.   /* Azimuth field.                                     */
  916.   degrees = info_azim[objno]/CONV;
  917.   sprintf(numbuf, "%5.1f°", (double)degrees);
  918.   dbox_setfield(d_info, azim_field, numbuf);
  919.  
  920.   /* Object number.                                     */
  921.   sprintf(numbuf, "Object %i", objno+1);
  922.   dbox_setfield(d_info, info_objno, numbuf);
  923.  
  924.   /* Write INFOBOX_LINES lines of text.                 */
  925.   for (i=0; i < INFOBOX_LINES; i++)
  926.     dbox_setfield(d_info, info_textline+i, info_lineptr[objno][i]);
  927.  
  928.   return;
  929. }
  930.  
  931.  
  932. static void infobox_buttons(int nobjects)
  933. {
  934. /* Deal with clicks on buttons in info box.  Change     */
  935. /* back and forth amongst the nobjects objects as       */
  936. /* requested by the user.                               */
  937. /* The object initially showing is number nobjects-1.   */
  938.  
  939. int obj = nobjects-1; /* No. of object showing initially*/
  940. int  response;   /* Latest field clicked by user.       */
  941. BOOL keep_open;  /* TRUE if dbox to remain open after   */
  942.                  /* this button is processed.           */
  943.                                                           
  944. /* Loop until dbox is to close.                         */
  945.   do
  946.   {
  947.  
  948.   /* Get field number of latest click.                  */
  949.     response = dbox_fillin(d_info);
  950.  
  951.   /* Take appropriate action.                           */
  952.     switch (response) {
  953.  
  954.       case info_cancel:
  955.       /* Dbox cancelled. Just close it.                 */
  956.         keep_open = FALSE;
  957.         break;
  958.  
  959.       case info_ok:
  960.       /* Finished with dbox.                            */
  961.         keep_open = dbox_persist();
  962.         break;
  963.  
  964.       case info_select:
  965.       /* Select the object whose details are currently  */
  966.       /* on display.                                    */
  967.         /* Set id field of 'selection' to specify which */
  968.         /* object is meant.                             */
  969.         selection.id = info_id[obj];
  970.         /* Call appropriate Select function to fill in  */
  971.         /* the remaining fields.                        */
  972.         module_fns[info_modno[obj]].selectfn(window_selection);
  973.         /* Establish the new selection.                 */
  974.         establish_new_selection(info_modno[obj]);
  975.         keep_open = dbox_persist();
  976.         break;
  977.  
  978.       case info_uparrow:
  979.       /* Display info on next object.                   */
  980.         obj = (obj+1)%nobjects;
  981.         infobox_load(obj);
  982.         keep_open = TRUE;
  983.         break;
  984.  
  985.       case info_dnarrow:
  986.       /* Display info on previous object.               */
  987.         obj = (obj+nobjects-1)%nobjects;
  988.         infobox_load(obj);
  989.         keep_open = TRUE;
  990.         break;
  991.  
  992.       default:
  993.       /* Unknown response.  Do nothing.                 */
  994.         keep_open = TRUE;
  995.         break;
  996.  
  997.     }
  998.  
  999.   } while (keep_open);
  1000.  
  1001.   return;
  1002. }
  1003.  
  1004.  
  1005. /*------------------------------------------------------*/
  1006. /*                 Re-draw a window                     */
  1007. /*------------------------------------------------------*/
  1008.  
  1009. static void redraw_window(wimp_w handle, sv_windata *wd)
  1010. {
  1011.   int more, x_orig, y_orig;
  1012.   wimp_redrawstr r;
  1013.   wimp_box to_redraw;
  1014.  
  1015.   r.w = handle;
  1016.   wimpt_noerr(wimp_redraw_wind(&r, &more));
  1017.   /* Work out screen coords of work area origin.        */
  1018.   x_orig = r.box.x0 - r.scx;
  1019.   y_orig = r.box.y1 - r.scy;
  1020.  
  1021.   while (more)
  1022.   {
  1023.     /* Move graphics origin to coincide with work       */
  1024.     /* area origin.                                     */
  1025.     bbc_origin(x_orig,y_orig);
  1026.  
  1027.     /* Get work area coords of rectangle to re-draw     */
  1028.     coords_offsetbox(&r.g, -x_orig, -y_orig, &to_redraw);
  1029.  
  1030.     /* Draw requested part of window.                   */
  1031.     wimpt_noerr(draw_window(wd, to_redraw));
  1032.  
  1033.     /* Move graphics origin back again                  */
  1034.     bbc_origin(0,0);
  1035.  
  1036.     /* Get next rectangle to do.                        */
  1037.     wimpt_noerr(wimp_get_rectangle(&r, &more));
  1038.   }
  1039.  
  1040.   return;
  1041. }
  1042.  
  1043.  
  1044. /*------------------------------------------------------*/
  1045. /*  Function to plot (or print) the specified part of a */
  1046. /*                        window.                       */
  1047. /*------------------------------------------------------*/
  1048. static os_error *draw_window(sv_windata *wd, wimp_box to_do)
  1049. {
  1050.   os_error *errptr;
  1051.   plotobj *p;
  1052.   plotobj *pstart = wd->listptr;  /*Start of plot list. */
  1053.   plotobj *pend   = pstart + wd->number; /*End of list. */
  1054.  
  1055. /*......................................................*/
  1056. /*       Plot the objects in the plotting list.         */
  1057. /*......................................................*/
  1058.  
  1059. /* Only plot those whose bounding box overlaps the      */
  1060. /* rectangle to be drawn.                               */
  1061. /* Do not plot if parent module is currently disabled.  */
  1062.  
  1063.   for(p = pstart; p < pend; p++)
  1064.     if (coords_boxesoverlap(&to_do, &p->bounds) &&\
  1065.       enabled[p->module])
  1066.     {
  1067.       errptr = p->plotfn(p->wind_x, p->wind_y, p->id);
  1068.       if (errptr != NULL) return errptr;
  1069.     }
  1070. /*......................................................*/
  1071.  
  1072. /* Also do the following, without bothering to check    */
  1073. /* whether the requested rectangle includes them:       */
  1074.  
  1075. /* Draw frame around selected object, if it exists      */
  1076. /* and is currently in window.                          */
  1077.   if (selection_exists && wd->selnum != NOWHERE)
  1078.   {
  1079. /* Get x and y coords of selected object.               */
  1080.     int sel_x = (pstart + wd->selnum)->wind_x;
  1081.     int sel_y = (pstart + wd->selnum)->wind_y;
  1082. /* Set foreground graphics colour to Wimp colour 0      */
  1083.     errptr = sv_setcolour(0);
  1084.     if (errptr != NULL) return errptr;
  1085. /* Draw frame, after shifting it to coords of           */
  1086. /* selected object.                                     */
  1087.     errptr = bbc_rectangle(\
  1088.       wd->sel_frame.x + sel_x, wd->sel_frame.y + sel_y,\
  1089.       wd->sel_frame.w, wd->sel_frame.h);
  1090.     if (errptr != NULL) return errptr;
  1091.   }
  1092.  
  1093. /* Call fn to re-draw compass points, and return.       */
  1094.   return (wd->compass_drawfn)();
  1095.  
  1096. }
  1097.  
  1098.  
  1099. /*------------------------------------------------------*/
  1100. /*   Toggle state of window between open and closed.    */
  1101. /*   Update Open/Closed flag appropriately.             */
  1102. /*------------------------------------------------------*/
  1103.  
  1104. static void toggle_window(wimp_w handle, BOOL *open)
  1105. {
  1106.   if (*open)
  1107.     /* Close the window */
  1108.   {
  1109.     wimpt_noerr(wimp_close_wind(handle));
  1110.     *open = FALSE;
  1111.   }
  1112.   else
  1113.   {
  1114.     /* Get window state */
  1115.     wimp_wstate state;
  1116.     if (wimpt_complain(wimp_get_wind_state(handle, &state)) == 0)
  1117.     {
  1118.       /* Open the window, in front of all others */
  1119.       state.o.behind = -1;
  1120.       wimpt_noerr(wimp_open_wind(&state.o));
  1121.       *open = TRUE;
  1122.     }
  1123.   }
  1124. }
  1125.  
  1126.  
  1127.  
  1128. /********************************************************/
  1129. /*         Specific Window-handling Functions           */
  1130. /********************************************************/
  1131.  
  1132. /*------------------------------------------------------*/
  1133. /*         Draw compass points for Horiz window         */
  1134. /*------------------------------------------------------*/
  1135.  
  1136. static os_error *draw_horiz_compass(void)
  1137. {
  1138.   static int bar_y0 = -4; /* Min y coord of horiz bar.  */
  1139.   static int bar_wd =  3; /* "Width" of horiz bar.      */
  1140.  
  1141.   static int tik_y0 =-12; /* Min y coord of tick mark.  */
  1142.   static int tik_ht =  7; /* "Height" of tick mark.     */
  1143.   static int tik_os = -1; /* x offset of LHS of tick mk */
  1144.   static int tik_wd =  2; /* "Width" of tick mark.      */
  1145.  
  1146.   static int lab_dn =-14; /* Down shift to print label  */
  1147.   static int lab_lt = -8; /* Fixed left shift before    */
  1148.                           /* printing label             */
  1149.   static int chr_sh =  8; /* Left shift for each        */
  1150.                           /* additional label character */
  1151.  
  1152.   int i;     /* Counter for drawing compass point labels*/
  1153.   int x_pos; /* Horizontal position of compass ticks.   */
  1154.   char *ptr; /* Pointer to text of compass label.       */
  1155.   int left;  /* Total left shift before printing label. */
  1156.  
  1157.   os_error *errptr;
  1158.   
  1159.  
  1160. /* Set foreground graphics colour to Wimp colour 0      */
  1161.   errptr = sv_setcolour(0);
  1162.   if (errptr != NULL) return errptr;
  1163.  
  1164. /* Draw a horizontal bar across whole window to denote  */
  1165. /* horizon.                                             */
  1166.   errptr = bbc_rectanglefill(horiz_size.x0,bar_y0,width,bar_wd);
  1167.   if (errptr != NULL) return errptr;
  1168.  
  1169. /* Draw in the NDIR compass point labels                */
  1170.   for (i=0; i<NDIR; i+=1)
  1171.   {
  1172.     /* Position of tick mark.                           */
  1173.     x_pos = x_h(direction[i]);
  1174.  
  1175.     /* Calculate left shift.                            */
  1176.     left = lab_lt - compass[i].len * chr_sh;
  1177.  
  1178.     /* Draw tick mark.                                  */
  1179.     errptr = bbc_rectanglefill(x_pos + tik_os, tik_y0, tik_wd, tik_ht);
  1180.     if (errptr != NULL) return errptr;
  1181.  
  1182.     /* Move to print position.                          */
  1183.     errptr = bbc_moveby(left, lab_dn);
  1184.     if (errptr != NULL) return errptr;
  1185.  
  1186.     /* Point to text of label, and print it one char    */
  1187.     /* at a time.                                       */
  1188.     for (ptr = compass[i].name; *ptr; ptr += 1)
  1189.       {errptr = bbc_vdu((int)*ptr);
  1190.        if (errptr != NULL) return errptr;}
  1191.  
  1192.   }
  1193.  
  1194.   return NULL;
  1195. }
  1196.  
  1197.  
  1198. /*------------------------------------------------------*/
  1199. /*         Draw compass points for Vert window.         */
  1200. /*------------------------------------------------------*/
  1201.  
  1202. static os_error *draw_vert_compass(void)
  1203. {
  1204.   int i;
  1205.   int x,y;
  1206.   int lab_dn =-12;
  1207.   int lab_lt =  8;
  1208.   int chr_sh =  8;
  1209.   int left;
  1210.   char *ptr;
  1211.  
  1212.   os_error *errptr;
  1213.  
  1214. /* Set foreground graphics colour to Wimp colour 0      */
  1215.   errptr = sv_setcolour(0);
  1216.   if (errptr != NULL) return errptr;
  1217.  
  1218. /* Plot the Draw Path set up earlier.                   */
  1219. /* If plotting to screen, use the semi-digested form    */
  1220. /* for speed.                                           */
  1221.   if (!printing)
  1222.   {
  1223.     draw_regsS.r[0] = (int)semi;
  1224.     errptr = os_swix(sv_XDraw_ProcessPath, &draw_regsS);
  1225.   }
  1226.   else
  1227. /* If printing, process the 'raw' path to take full     */
  1228. /* advantage of the resolution of the printer.          */
  1229.   {
  1230.     draw_regsP.r[0] = (int)path;
  1231.     errptr = os_swix(sv_XDraw_Stroke, &draw_regsP);
  1232.   }
  1233.   if (errptr != NULL) return errptr;
  1234.  
  1235. /* Plot the NDIR compass labels.                        */
  1236.   for(i=0; i<NDIR; i++)
  1237.   {
  1238.     xy_v(compass_alt*(REAL)0.94, direction[i], &x, &y);
  1239.     y -= lab_dn;
  1240.     left = lab_lt + compass[i].len * chr_sh;
  1241.     x -= left;
  1242.  
  1243.     /* Move to print position.                          */
  1244.     errptr = bbc_move(x,y);
  1245.     if (errptr != NULL) return errptr;
  1246.  
  1247.     /* Point to text of label, and print it one char    */
  1248.     /* at a time.                                       */
  1249.     for (ptr = compass[i].name; *ptr; ptr += 1)
  1250.       {errptr = bbc_vdu((int)*ptr);
  1251.        if (errptr != NULL) return errptr;}
  1252.   }
  1253.  
  1254.   return NULL;
  1255. }
  1256.  
  1257. /********************************************************/
  1258. /*                    Plotting Functions                */
  1259. /********************************************************/
  1260.  
  1261. static int x_h(REAL azimuth)
  1262. {
  1263.   /*               - Horizontal window -                */
  1264.   /* Calculate x-coordinate corresponding to given      */
  1265.   /* azimuth, taking into account the current direction */
  1266.   /* of view.                                           */
  1267.  
  1268.   int x;
  1269.   REAL x_floating = factor * (azimuth - ob_data.ang);
  1270.  
  1271.   x = (int)floor((double)x_floating);
  1272.   if      (x <  horiz_size.x0)
  1273.     x += width;
  1274.   else if (x >= horiz_size.x1)
  1275.     x -= width;
  1276.  
  1277.   return x;
  1278. }
  1279.  
  1280.  
  1281. static int y_h(REAL altitude)
  1282. {
  1283.   /*               - Horizontal window -                */
  1284.   /* Calculate y-coordinate corresponding to given      */
  1285.   /* altitude.                                          */
  1286.  
  1287.   return (int)(factor*altitude);
  1288. }
  1289.  
  1290.  
  1291. static void xy_v(REAL altitude, REAL azimuth, int *xptr, int *yptr)
  1292. {
  1293. /*                   - Vert window -                    */
  1294. /* Calculate x- and y-coordinate from given altitude    */
  1295. /* and azimuth, taking into account current direction   */
  1296. /* of view.                                             */
  1297.  
  1298.   REAL theta = azimuth - ob_data.ang;
  1299.   REAL r     = factor*(PIby2 - altitude);
  1300.  
  1301.   if (xptr != NULL)
  1302.     *xptr = (int)floor( (double)r * \
  1303.                     sin((double)theta));
  1304.   if (yptr != NULL)
  1305.     *yptr = (int)floor(-(double)r * \
  1306.                     cos((double)theta));
  1307.   return;
  1308. }
  1309.  
  1310.  
  1311. /*------------------------------------------------------*/
  1312. /*         Functions for constructing Draw Path         */
  1313. /*------------------------------------------------------*/
  1314.  
  1315. static void sv_Draw_circle(int r)
  1316. {
  1317.   int ar = (int)((REAL)0.55228475*(REAL)r);
  1318.  
  1319. /* Move to (r,0).                                       */
  1320.   sv_Draw_move(r,0);
  1321.  
  1322. /* Set up four Bezier curves describing one quadrant each*/
  1323.   sv_Draw_bezi(  r, ar,   ar,  r,   0, r);
  1324.   sv_Draw_bezi(-ar,  r,   -r, ar,  -r, 0);
  1325.   sv_Draw_bezi( -r,-ar,  -ar, -r,   0,-r);
  1326.   sv_Draw_bezi( ar, -r,    r,-ar,   r, 0);
  1327.  
  1328.   return;
  1329. }
  1330.  
  1331. static void sv_Draw_move(int x, int y)
  1332. {
  1333.   if (pathptr+3 > PathMax) return;
  1334.   path[pathptr++] = 2;
  1335.   path[pathptr++] = x;
  1336.   path[pathptr++] = y;
  1337.  
  1338.   return;
  1339. }
  1340.  
  1341. static void sv_Draw_line(int x, int y)
  1342. {
  1343.   if (pathptr+3 > PathMax) return;
  1344.   path[pathptr++] = 8;
  1345.   path[pathptr++] = x;
  1346.   path[pathptr++] = y;
  1347.  
  1348.   return;
  1349. }
  1350.  
  1351. static void sv_Draw_bezi(int x1, int y1, int x2, int y2, int x3, int y3)
  1352. {
  1353.   if (pathptr+7 > PathMax) return;
  1354.   path[pathptr++] = 6;
  1355.   path[pathptr++] = x1;
  1356.   path[pathptr++] = y1;
  1357.   path[pathptr++] = x2;
  1358.   path[pathptr++] = y2;
  1359.   path[pathptr++] = x3;
  1360.   path[pathptr++] = y3;
  1361.  
  1362.   return;
  1363. }
  1364.  
  1365. static void sv_Draw_endp(void)
  1366. {
  1367.   path[pathptr]   = 0;
  1368.   path[pathptr+1] = 0;
  1369.  
  1370.   return;
  1371. }
  1372.  
  1373.  
  1374. /*------------------------------------------------------*/
  1375. /*    Function for setting graphics fore/back colour.   */
  1376. /*------------------------------------------------------*/
  1377. os_error *sv_setcolour(int colour)
  1378. {
  1379.  
  1380. /* If not currently printing, pass colour unaltered to  */
  1381. /* Wimp_SetColour.                                      */
  1382.   if (!printing) return wimp_setcolour(colour);
  1383.  
  1384. /* If printing, use printer palette instead of screen   */
  1385. /* palette.                                             */
  1386.   {
  1387.   /* Using a variable of type ctrans_type, split the    */
  1388.   /* given argument into a Wimp colour number (0 - 15), */
  1389.   /* a GCOL action, and a Foreground / Background flag. */
  1390.     ctrans_type ctrans;
  1391.     wimp_paletteword entry;
  1392.     int w_colour;
  1393.     int fore_back;
  1394.     int gcol_act;
  1395.     int gcol_out;
  1396.  
  1397.     ctrans.argument = colour;
  1398.     w_colour  = (int)ctrans.fields.w_colour;
  1399.     fore_back = (int)ctrans.fields.foreback << 7;
  1400.     gcol_act  = (int)ctrans.fields.gcol_act;
  1401.  
  1402.   /* Get the printer palette entry corresponding to     */
  1403.   /* w_colour.                                          */
  1404.     entry     = printer_palette[w_colour];
  1405.  
  1406.   /* Set the GCOL using the appropriate ColourTrans call*/
  1407.     return colourtran_setGCOL(entry, fore_back, gcol_act, &gcol_out);
  1408.  
  1409.   }
  1410. }
  1411.  
  1412. /*------------------------------------------------------*/
  1413. /* Function to plot a Mode 12 or Mode 20 sprite, scaled */
  1414. /*            to suit the current screen mode.          */
  1415. /*------------------------------------------------------*/
  1416. os_error *sv_plotsprite(sprite_id *id,
  1417.                        BOOL mode20,
  1418.                        int x, int y,
  1419.                        int col, int row)
  1420. {
  1421.   int gcol_act = 8; /*Plot with overwriting, using mask.*/
  1422.  
  1423. /* Calculate plotting position in OS units which will   */
  1424. /* place the Key Pixel at the desired coords (x,y).     */
  1425.   x -= col * 2;
  1426.   y -= row * (mode20? 2 : 4);
  1427.  
  1428. /* The pixel translation table to use when plotting     */
  1429. /* depends on whether the output is going to the screen */
  1430. /* or the printer.                                      */
  1431. /* Also, use scaling factors appropriate to sprite mode.*/
  1432.  
  1433.   return sprite_put_scaled(resspr_area(),
  1434.                            id,
  1435.                            gcol_act,
  1436.                            x, y,
  1437.                            (mode20?   &s_factors20: &s_factors12),
  1438.                            (printing? pr_pixtrans : sc_pixtrans));
  1439. }
  1440.  
  1441. /*------------------------------------------------------*/
  1442. /*  Function to re-calculate Sprite scaling and trans-  */
  1443. /*  lation tables, following a change of screen mode.   */
  1444. /*------------------------------------------------------*/
  1445. static os_error *sprite_tables(void)
  1446. {
  1447. /* Set scaling factors for a mode 12 sprite.            */
  1448.   s_factors12.xmag = 2;
  1449.   s_factors12.ymag = 4;
  1450.   s_factors12.xdiv = wimpt_dx();
  1451.   s_factors12.ydiv = wimpt_dy();
  1452.  
  1453. /* Set scaling factors for a mode 20 sprite.            */
  1454.   s_factors20.xmag = 2;
  1455.   s_factors20.ymag = 2;
  1456.   s_factors20.xdiv = wimpt_dx();
  1457.   s_factors20.ydiv = wimpt_dy();
  1458.  
  1459. /* Set pixel translation table for plotting to screen.  */
  1460. /* Use same table for mode 12 and mode 20 sprites.      */
  1461.   return colourtran_select_table(20,
  1462.                                  screen_palette,
  1463.                                  -1,
  1464.                                  (wimp_paletteword *)(-1),
  1465.                                  sc_pixtrans);
  1466. }
  1467.  
  1468. /********************************************************/
  1469. /*                    Main Menu Handling                */
  1470. /********************************************************/
  1471.  
  1472. /*------------------------------------------------------*/
  1473. /*                    pre menu proc                     */
  1474. /*------------------------------------------------------*/
  1475.  
  1476. static menu main_premenuproc(void *handle)
  1477. {
  1478.   BOOL fade_select_item;   /* TRUE if Select item needs */
  1479.                            /* to be faded.              */
  1480.  
  1481.   /* Fade Horiz and Vert items if observer details have */
  1482.   /* not been checked yet.                              */
  1483.   /* Tick them if the corresponding window is open now. */
  1484.   menu_setflags(main_menu, item_horiz, \
  1485.     horiz_data.open, !observer_verified);
  1486.   menu_setflags(main_menu, item_vert, \
  1487.     vert_data.open, !observer_verified);
  1488.  
  1489.   /* Fade Print item if observer details have not      */
  1490.   /* been verified yet.                                */
  1491.   menu_setflags(main_menu, item_print, 0, !observer_verified);
  1492.  
  1493.   /* Fade Select item if observer details have not      */
  1494.   /* been verified yet, or if there are no items in the */
  1495.   /* Select sub-menu.                                   */
  1496.   fade_select_item = !observer_verified || (select_count == 0);
  1497.   menu_setflags(main_menu, item_select,  0, fade_select_item);
  1498.  
  1499.   /* Fade View item if ob. details not verified yet.    */
  1500.   menu_setflags(main_menu, item_view,    0, !observer_verified);
  1501.  
  1502.   /* Fade Details and Cancel if there is no selection.  */
  1503.   menu_setflags(main_menu, item_details, 0, !selection_exists);
  1504.   menu_setflags(main_menu, item_cancel,  0, !selection_exists);
  1505.  
  1506.   /* Fade 'View sel.' sub-menu items according to      */
  1507.   /* whether or not there is a selection, and (if      */
  1508.   /* there is) when it can be seen.                    */
  1509.   menu_setflags(view_sub_menu, view_item_now,\
  1510.     0, !(selection_exists && selection.now));
  1511.   menu_setflags(view_sub_menu, view_item_rising,\
  1512.     0, !(selection_exists && selection.rising));
  1513.   menu_setflags(view_sub_menu, view_item_setting,\
  1514.     0, !(selection_exists && selection.setting));
  1515.   menu_setflags(view_sub_menu, view_item_culminating,\
  1516.     0, !(selection_exists && selection.culminating));
  1517.  
  1518.   return main_menu;
  1519. }
  1520.  
  1521. /*------------------------------------------------------*/
  1522. /*               handle hits on main menu               */
  1523. /*------------------------------------------------------*/
  1524.  
  1525. static void main_menuproc(void *handle, char *hit)
  1526. {
  1527.   switch (hit[0])
  1528.   {
  1529.     case item_info:
  1530.       sv_info_about_program();
  1531.       break;
  1532.  
  1533.     case item_observer:
  1534.       /* Make sure menu has gone. It wouldn't     */
  1535.       /* reflect changes made by the Observer dbox*/
  1536.       event_clear_current_menu();
  1537.       observer();
  1538.       break;
  1539.  
  1540.     case item_display:
  1541.       invoke_displayfns(hit);
  1542.       break;
  1543.  
  1544.     case item_horiz:
  1545.       /* Open window if it's closed, & vice versa */
  1546.       toggle_window(horiz_handle, &horiz_data.open);
  1547.       break;
  1548.  
  1549.     case item_vert:
  1550.       /* Open window if it's closed, & vice versa */
  1551.       toggle_window(vert_handle, &vert_data.open);
  1552.       break;
  1553.  
  1554.     case item_print:
  1555.       print_sub_menuproc(hit);
  1556.       break;
  1557.  
  1558.     case item_select:
  1559.       invoke_selectfns(hit);
  1560.       break;
  1561.  
  1562.     case item_view:
  1563.       view_sub_menuproc(hit);
  1564.       break;
  1565.  
  1566.     case item_details:
  1567.       sel_info();
  1568.       break;
  1569.  
  1570.     case item_cancel:
  1571.       cancel_selection();
  1572.       break;
  1573.  
  1574.     case item_quit:
  1575.       exit(0);
  1576.       break;
  1577.  
  1578.     default:
  1579.       break;
  1580.   }
  1581.  
  1582.   return;
  1583. }
  1584.  
  1585.  
  1586. /**** Functions dealing with printing of window data ****/
  1587.  
  1588. /*------------------------------------------------------*/
  1589. /*      Function to handle hits on Print sub-menu       */
  1590. /*------------------------------------------------------*/
  1591. static void print_sub_menuproc(char *hit)
  1592. {
  1593.   switch (hit[1])
  1594.   {
  1595.     case print_item_horizq:
  1596.     /* Print a quadrant of the Horiz window.            */
  1597.     if (!pdriver_installed()) return;
  1598.     printing = TRUE;
  1599.     print_hquadrant();
  1600.     printing = FALSE;
  1601.     break;
  1602.  
  1603.     case print_item_vertc:
  1604.     /* Print the Vert window.                           */
  1605.     if (!pdriver_installed()) return;
  1606.     printing = TRUE;
  1607.     print_vertical();
  1608.     printing = FALSE;
  1609.     break;
  1610.  
  1611.     default:
  1612.     /* Unknown option.  Do nothing.                     */
  1613.     break;
  1614.  
  1615.   }
  1616.  
  1617.   return;
  1618. }
  1619.  
  1620. /* ********* Functions dealing with printing ********** */
  1621.  
  1622. /*------------------------------------------------------*/
  1623. /*    Function to print a quadrant of the Horizontal    */
  1624. /*  window (centered on the current direction of view). */
  1625. /*------------------------------------------------------*/
  1626. static void print_hquadrant(void)
  1627. {
  1628.   int left_dir;   /* Direction of view corresponding to */
  1629.                   /* left side of window area.          */
  1630.   int right_dir;  /* Direction of view corresponding to */
  1631.                   /* right side of window area.         */
  1632.   int wider = 64; /* Amount to widen window area to make*/
  1633.                   /* sure it includes a compass label at*/
  1634.                   /* each side as well as in the middle.*/
  1635.  
  1636.   int title_ht = 64; /* Height of box containing title. */
  1637.  
  1638. /* Specify the area of the window to be plotted:        */
  1639.  
  1640. /* Find the directions of view corresponding to         */
  1641. /* the left and right sides of the print region.        */
  1642.   left_dir  = (ob_data.dir + NDIR - 1) % NDIR;
  1643.   right_dir = (ob_data.dir        + 1) % NDIR;
  1644. /* Then specify the window area required.               */
  1645.   horiz_data.wind_area.x0 = x_h(direction[left_dir])  - wider;
  1646.   horiz_data.wind_area.y0 = horiz_size.y0;
  1647.   horiz_data.wind_area.x1 = x_h(direction[right_dir]) + wider;
  1648.   horiz_data.wind_area.y1 = horiz_size.y1;
  1649.  
  1650. /* Print the specified part of the window, with title   */
  1651. /* and frame.                                           */
  1652.   print_window(&horiz_data, title_ht);
  1653.  
  1654.   return;
  1655. }
  1656.  
  1657. /*------------------------------------------------------*/
  1658. /* Function to build a title string for the Horiz window*/
  1659. /*------------------------------------------------------*/
  1660. static void build_horiz_title(char *title, BOOL font_ok)
  1661. {
  1662. /* Write current observer details into a string.  Put   */
  1663. /* everything onto a single line of text.               */
  1664.   ob_info_string(title, ONE_LINE, font_ok);
  1665.   return;
  1666. }
  1667.  
  1668. /*------------------------------------------------------*/
  1669. /* Function to write out the title for the Horiz window.*/
  1670. /* Return any os_error which occurs.                    */
  1671. /*------------------------------------------------------*/
  1672. static os_error *write_horiz_title(char *title, BOOL font_ok)
  1673. {
  1674.   int x, y;
  1675.  
  1676.   x = horiz_data.wind_area.x0 + 8;
  1677.   y = horiz_data.wind_area.y1 + 48;
  1678.   return write_title(x, y, title, font_ok);
  1679. }
  1680.  
  1681. /*------------------------------------------------------*/
  1682. /*    Function to print the entire Vertical window.     */
  1683. /*------------------------------------------------------*/
  1684. static void print_vertical(void)
  1685. {
  1686.   int title_ht = 160; /* Height of box containing title.*/
  1687.  
  1688. /* Specify the window area required (= whole area).     */
  1689.   vert_data.wind_area = vert_size;
  1690.  
  1691. /* Print the specified part of the window, with title   */
  1692. /* and frame.                                           */
  1693.   print_window(&vert_data, title_ht);
  1694.  
  1695.   return;
  1696. }
  1697.  
  1698. /*------------------------------------------------------*/
  1699. /* Function to build a title string for the Vert window.*/
  1700. /*------------------------------------------------------*/
  1701. static void build_vert_title(char *title, BOOL font_ok)
  1702. {
  1703. /* Write current observer details into a string.  Split */
  1704. /* the info into three separate lines.                  */
  1705.   ob_info_string(title, THREE_LINES, font_ok);
  1706.   return;
  1707. }
  1708.  
  1709. /*------------------------------------------------------*/
  1710. /* Function to write out the title for the Vert window. */
  1711. /* Return any os_error which occurs.                    */
  1712. /*------------------------------------------------------*/
  1713. static os_error *write_vert_title(char *title, BOOL font_ok)
  1714. {
  1715.   os_error *errptr;
  1716.   int x, y;
  1717.  
  1718.   x = vert_data.wind_area.x0 +  62;
  1719.   y = vert_data.wind_area.y1 + 144;
  1720.   errptr = write_title(x, y, title, font_ok);
  1721.   if (errptr != NULL) return errptr;
  1722.   title += strlen(title)+1;
  1723.  
  1724.   x = vert_data.wind_area.x0 +  62;
  1725.   y = vert_data.wind_area.y1 +  96;
  1726.   errptr = write_title(x, y, title, font_ok);
  1727.   if (errptr != NULL) return errptr;
  1728.   title += strlen(title)+1;
  1729.  
  1730.   x = vert_data.wind_area.x0 +  62;
  1731.   y = vert_data.wind_area.y1 +  48;
  1732.   return write_title(x, y, title, font_ok);
  1733. }
  1734.  
  1735. /*------------------------------------------------------*/
  1736. /*  Function to write out a title string, starting at   */
  1737. /*    the specified coords (in OS units).  Use anti-    */
  1738. /*    aliassed font if BOOL flag is TRUE, system font   */
  1739. /*                      otherwise.                      */
  1740. /*------------------------------------------------------*/
  1741. static os_error *write_title(int x, int y, char *title, BOOL font_ok)
  1742. {
  1743.   int counter;
  1744.   os_error *errptr;
  1745.  
  1746.   if (font_ok) return font_paint(title, font_OSCOORDS, x, y-VSHIFT);
  1747.  
  1748.   errptr = bbc_move(x,y);
  1749.   if (errptr != NULL) return errptr;
  1750.   return write_string(title, &counter);
  1751. }
  1752.  
  1753. /*------------------------------------------------------*/
  1754. /*  Function to print (part of) a window using RISC OS  */
  1755. /*                  printer drivers.                    */
  1756. /*------------------------------------------------------*/
  1757. static void print_window(sv_windata *wd, int title_ht)
  1758. {
  1759.   os_regset OSFind_regs;     /* For OS_Find call which  */
  1760.   char file[] = "printer:";  /* opens "printer:" file.  */
  1761.   int jhandle;               /* Job handle.             */
  1762.   int old;                   /* Old job handle.         */
  1763.   int jdummy;                /* For unwanted job handle.*/
  1764.  
  1765.   int frame_lt;      /* Left of frame around window.    */
  1766.   int frame_bt;      /* Bottom of frame around window.  */
  1767.   int frame_wd;      /* Width of frame around window.   */
  1768.   int frame_ht;      /* Height of frame around window   */
  1769.                      /* (includes title box).           */
  1770.   int margin = 4;    /* All-round safety margin, to make*/
  1771.                      /* sure complete image gets printed*/
  1772.  
  1773.   int bgcol = PAL_WHITE;  /* Background colour (white). */
  1774.  
  1775.   print_box to_print;/* Region to be printed.  Includes */
  1776.                      /* window area, frame, title box & */
  1777.                      /* all-round safety margin.        */
  1778.  
  1779.   wimp_box to_draw;           /* Region that PDriver    */
  1780.                               /* wants drawn.           */
  1781.   int more;                   /* Pdriver's flag.        */
  1782.   int id;                     /* ID of rectangle.       */
  1783.   wimp_box clip;              /* Pdriver's clip rectang.*/
  1784.  
  1785.   char title[TITLE_LEN];      /* Title string for windw.*/
  1786.   font font_handle;           /* Font handle.           */
  1787.   BOOL font_open = FALSE;     /* Is a font file open?   */
  1788.   wimp_paletteword foregr;    /* Foreground pal. entry. */
  1789.   wimp_paletteword backgr;    /* Background pal. entry. */
  1790.   int off = 0;                /* Max intermediate colors*/
  1791.  
  1792. /* Error pointer returned by RISC_OSLib routines:       */
  1793.   os_error *errptr;
  1794. /* Temp storage for errors during exit handling:        */
  1795.   os_error *ptrsav_e = NULL;   /* Pointer.              */
  1796.   os_error err_blk_e;          /* Error block.          */
  1797. /* Flag indicating if print job has started:            */
  1798.   BOOL job_started = FALSE;
  1799. /* Temp storage for errors which occur while printing:  */
  1800.   os_error *ptrsav_p = NULL;   /* Pointer.              */
  1801.   os_error err_blk_p = {       /* Error block (contain- */
  1802.    255,                        /* ing a default message)*/
  1803.    "Error (print cancelled)"};
  1804. /* Error block for unreported Escapes.                  */
  1805.   os_error unrep_esc = {
  1806.    255,
  1807.    "Escape. (Print cancelled.)"};
  1808.  
  1809.  
  1810. /* Open "printer:" file and record the returned handle. */
  1811.   OSFind_regs.r[0] = 0x8F;
  1812.   OSFind_regs.r[1] = (int)file;
  1813.   wimpt_noerr(os_find(&OSFind_regs));
  1814.   jhandle = OSFind_regs.r[0];
  1815.  
  1816. /* Make sure returned handle is valid.                  */
  1817.   if (jhandle == 0)
  1818.     werr(FATAL, "Invalid 'printer:' handle");
  1819.  
  1820. /* Turn on hourglass.                                   */
  1821.   visdelay_begin();
  1822.  
  1823. /* Start the print job.                                 */
  1824.   errptr = print_selectjob(jhandle, "SkyViewPr", &old);
  1825.   if (errptr != NULL) goto error_exit;
  1826.   job_started = TRUE;
  1827.  
  1828. /* Set pixel translation table for plotting sprites to  */
  1829. /* printer.  Use same table for mode 12 sprites as for  */
  1830. /* mode 20 sprites.                                     */
  1831.   errptr = colourtran_select_table(20,
  1832.                                    printer_palette,
  1833.                                    -1,
  1834.                                    (wimp_paletteword *)(-1),
  1835.                                    pr_pixtrans);
  1836.   if (errptr != NULL) goto error_exit;
  1837.  
  1838. /* Open font for title string.  If errors, use system   */
  1839. /* font instead.                                        */
  1840.   errptr = font_find(TITLE_FONT, FONT_SIZE, FONT_SIZE, 0, 0, &font_handle);
  1841.   font_open = (errptr == NULL);
  1842.  
  1843. /* Set anti-alias palette.                              */
  1844.   if (font_open)
  1845.   {
  1846.     foregr.word = PAL_BLACK;
  1847.     backgr.word = PAL_WHITE;
  1848.     errptr = colourtran_setfontcolours(&font_handle, &backgr, &foregr, &off);
  1849.     if (errptr != NULL) goto error_exit;
  1850.   }
  1851.  
  1852. /* Call function to build title string.                 */
  1853.   wd->title_buildfn(title, font_open);
  1854.  
  1855. /* Specify the rectangle to be plotted:                 */
  1856.  
  1857. /* Using the given wind_area, work out the coords of    */
  1858. /* the frame surrounding the window and the title.      */
  1859.   frame_lt = wd->wind_area.x0 - 1;
  1860.   frame_bt = wd->wind_area.y0 - 1;
  1861.   frame_wd = wd->wind_area.x1 - wd->wind_area.x0 + 1;
  1862.   frame_ht = wd->wind_area.y1 - wd->wind_area.y0 + 1 + title_ht;
  1863. /* Can now fill in the printer_box.                     */
  1864.   to_print.x0 = frame_lt - margin;
  1865.   to_print.y0 = frame_bt - margin;
  1866.   to_print.x1 = frame_lt + frame_wd + margin;
  1867.   to_print.y1 = frame_bt + frame_ht + margin;
  1868. /* Finally call PDriver_GiveRectangle.                  */
  1869. /* Abort printing if there is an error.                 */
  1870.   errptr = print_giverectangle\
  1871.                  (0, &to_print, &wd->matrix, &wd->posn, bgcol);
  1872.   if (errptr != NULL) goto error_exit;
  1873.  
  1874. /* Get series of rectangles to be drawn.                */
  1875.   errptr = print_drawpage(1, 0, NULL, (print_box *)&to_draw, &more, &id);
  1876.   if (errptr != NULL) goto error_exit;
  1877.  
  1878.   while (more != 0)
  1879.   {
  1880.  
  1881.   /* First do the following, without bothering to check */
  1882.   /* whether they are inside the requested rectangle:   */
  1883.   /* Set foreground colour.                             */
  1884.     errptr = sv_setcolour(0);
  1885.     if (errptr != NULL) goto error_exit;
  1886.   /* Draw the frame around the window.                  */
  1887.     errptr = bbc_rectangle(frame_lt, frame_bt, frame_wd, frame_ht);
  1888.     if (errptr != NULL) goto error_exit;
  1889.   /* Draw a line to mark the top of the window.         */
  1890.     errptr = bbc_move(frame_lt,          wd->wind_area.y1);
  1891.     if (errptr != NULL) goto error_exit;
  1892.     errptr = bbc_draw(frame_lt+frame_wd, wd->wind_area.y1);
  1893.     if (errptr != NULL) goto error_exit;
  1894.   /* Clear 'escape seen' flag.                          */
  1895.     _kernel_escape_seen();
  1896.   /* Call function to write title.                      */
  1897.     errptr = wd->title_writefn(title, font_open);
  1898.     if (errptr != NULL) goto error_exit;
  1899.   /* Check for unreported escape.                       */
  1900.     if (_kernel_escape_seen()) { errptr = &unrep_esc;
  1901.                                goto error_exit;}
  1902.  
  1903.   /* Then draw the window (if the requested rectangle   */
  1904.   /* overlaps it at all), setting the pdriver's clip    */
  1905.   /* rectangle so that nothing can spill over the frame.*/
  1906.     if (overlap(&to_draw, &wd->wind_area, &clip))
  1907.     {
  1908.       errptr = bbc_vdu(bbc_DefGraphWindow);
  1909.       if (errptr != NULL) goto error_exit;
  1910.       errptr = bbc_vduw(clip.x0);
  1911.       if (errptr != NULL) goto error_exit;
  1912.       errptr = bbc_vduw(clip.y0);
  1913.       if (errptr != NULL) goto error_exit;
  1914.      /* VDU clipping rectangle uses inclusive coords:   */
  1915.       errptr = bbc_vduw(clip.x1-1);
  1916.       if (errptr != NULL) goto error_exit;
  1917.       errptr = bbc_vduw(clip.y1-1);
  1918.       if (errptr != NULL) goto error_exit;
  1919.       errptr = draw_window(wd, clip);
  1920.       if (errptr != NULL) goto error_exit;
  1921.     }
  1922.  
  1923.   /* Reset clipping rectangle.                          */
  1924.     errptr = bbc_vdu(bbc_DefaultWindow);
  1925.     if (errptr != NULL) goto error_exit;
  1926.  
  1927.   /* Get next rectangle.                                */
  1928.     errptr = print_getrectangle((print_box *)&to_draw, &more, &id);
  1929.     if (errptr != NULL) goto error_exit;
  1930.   }
  1931.  
  1932. /* End the print job.                                   */
  1933.   errptr = print_endjob(jhandle);
  1934.   if (errptr != NULL) goto error_exit;
  1935.  
  1936. /* Normal exit:                                         */
  1937.  
  1938. /* Turn off hourglass.                                  */
  1939.   visdelay_end();
  1940. /* Re-select job which was active when this one started.*/
  1941.   errptr = print_selectjob(old, NULL, &jdummy);
  1942.   if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1943. /* Close "printer:" file.                               */
  1944.   OSFind_regs.r[0] = 0;
  1945.   OSFind_regs.r[1] = jhandle;
  1946.   errptr = os_find(&OSFind_regs);
  1947.   if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1948. /* Close font file.                                     */
  1949.   if (font_open)
  1950.   {
  1951.     errptr = font_lose(font_handle);
  1952.     if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1953.   }
  1954. /* If errors, report most recent, treating it as fatal. */
  1955.   wimpt_noerr(ptrsav_e);
  1956.  
  1957.   return;
  1958.  
  1959.  
  1960.   error_exit:
  1961.  
  1962. /* Store the error for reporting later.  If pointer is  */
  1963. /* obviously invalid, use the default message.          */
  1964.   if ((int)errptr <= 0x1C)
  1965.     ptrsav_p = &err_blk_p;
  1966.   else
  1967.     ptrsav_p = save_error(errptr, &err_blk_p);
  1968. /* Turn off hourglass.                                  */
  1969.   visdelay_end();
  1970. /* Cope with case where print job has started.          */
  1971.   if (job_started)
  1972.   {
  1973.   /* Abort the print job.                               */
  1974.     errptr = print_abortjob(jhandle);
  1975.     if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1976.   /*Re-select job which was active when this one started*/
  1977.     errptr = print_selectjob(old, NULL, &jdummy);
  1978.     if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1979.   }
  1980. /* Close "printer:" file.                               */
  1981.   OSFind_regs.r[0] = 0;
  1982.   OSFind_regs.r[1] = jhandle;
  1983.   errptr = os_find(&OSFind_regs);
  1984.   if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1985. /* Close font file.                                     */
  1986.   if (font_open)
  1987.   {
  1988.     errptr = font_lose(font_handle);
  1989.     if (errptr != NULL) ptrsav_e = save_error(errptr, &err_blk_e);
  1990.   }
  1991. /* If errors occurred during error handling, report the */
  1992. /* most recent, treating it as fatal.                   */
  1993.   wimpt_noerr(ptrsav_e);
  1994. /* Report the original error.                           */
  1995.   wimpt_reporterror(ptrsav_p, wimp_EOK);
  1996.  
  1997.   return;
  1998. }
  1999.  
  2000. /*-------------------------------------------------------*/
  2001. /*  Function to save the error block pointed to by ptr1  */
  2002. /*   into the block pointed to by ptr2.  Returns ptr2.   */
  2003. /*-------------------------------------------------------*/
  2004. static os_error *save_error(os_error *ptr1, os_error *ptr2)
  2005. {
  2006.   *ptr2 = *ptr1;
  2007.   return ptr2;
  2008. }
  2009.  
  2010. /*-------------------------------------------------------*/
  2011. /* Function to build a string containing current observer*/
  2012. /* details.  Returns one string containing everything if */
  2013. /* 'split' is FALSE, otherwise splits the info over 3    */
  2014. /* strings which follow one after the other in memory.   */
  2015. /* Adds extra spaces if split is false and a fancy font  */
  2016. /* is being used.                                        */
  2017. /*-------------------------------------------------------*/
  2018. static void ob_info_string(char *chrptr, BOOL split, BOOL font_ok)
  2019. {
  2020.   chrptr += sprintf(chrptr, "SkyView");
  2021.   if (split)
  2022.     chrptr += sprintf(chrptr, "%c", '\0');
  2023.   else if (font_ok)
  2024.     chrptr += sprintf(chrptr, "          ");
  2025.   else
  2026.     chrptr += sprintf(chrptr, "     ");
  2027.   chrptr += sprintf(chrptr, "Lat. %.1f° ",  (double)ob_data.lat10/10.0);
  2028.   chrptr += sprintf(chrptr, "%s,  ", (ob_data.isn? "N" : "S"));
  2029.   chrptr += sprintf(chrptr, "Long. %.1f° ", (double)ob_data.long10/10.0);
  2030.   chrptr += sprintf(chrptr, "%s",    (ob_data.isw? "W" : "E"));
  2031.   if (split)
  2032.     chrptr += sprintf(chrptr, "%c", '\0');
  2033.   else if (font_ok)
  2034.     chrptr += sprintf(chrptr, "          ");
  2035.   else
  2036.     chrptr += sprintf(chrptr, "     ");
  2037.   chrptr += sprintf(chrptr, "%02i/%02i/%i", ob_data.day, ob_data.month,
  2038.                                                          ob_data.year);
  2039.   chrptr += sprintf(chrptr, "  %02i:%02i", ob_data.hour, ob_data.min);
  2040.   if (ob_data.offset >= 0.0f)
  2041.     chrptr += sprintf(chrptr, "  (GMT + ");
  2042.   else
  2043.     chrptr += sprintf(chrptr, "  (GMT - ");
  2044.   chrptr += sprintf(chrptr, "%.1f h)", fabs((double)ob_data.offset));
  2045.   return;
  2046. }
  2047.  
  2048. /*-------------------------------------------------------*/
  2049. /* Function to write out a string as a sequence of VDU   */
  2050. /* commands.  Fills in *countptr with the number of      */
  2051. /* characters written, and returns any os_error which    */
  2052. /* occurred.                                             */
  2053. /*-------------------------------------------------------*/
  2054. static os_error *write_string(char *chrptr, int *countptr)
  2055. {
  2056.   os_error *errptr;
  2057.  
  2058.   errptr = sv_setcolour(0);
  2059.   if (errptr != NULL) return errptr;
  2060.  
  2061.   for (*countptr = 0; *chrptr; chrptr++, (*countptr)++)
  2062.     {errptr = bbc_vdu(*chrptr);
  2063.      if (errptr != NULL) return errptr;}
  2064.  
  2065.   return NULL;
  2066. }
  2067.  
  2068. /*------------------------------------------------------*/
  2069. /*  Function which sets the wimp_box *olap to the area  */
  2070. /* common to the wimp_boxes *b1 and *b2.  Returns FALSE */
  2071. /*   and leaves *olap undefined if *b1 and *b2 do not   */
  2072. /*           overlap.  Returns TRUE if all OK.          */
  2073. /*------------------------------------------------------*/
  2074. static BOOL overlap(wimp_box *b1, wimp_box *b2, wimp_box *olap)
  2075. {
  2076.   os_error olap_error = {            /* Define a new    */
  2077.     255,                             /* type of error.  */
  2078.     "Invalid printer rectangle"};
  2079.  
  2080. /* Check validity of *b1 and *b2.                       */
  2081.   if (b1->x0 > b1->x1 ||
  2082.       b1->y0 > b1->y1 ||
  2083.       b2->x0 > b2->x1 ||
  2084.       b2->y0 > b2->y1)
  2085.   {
  2086.     wimpt_reporterror(&olap_error, wimp_EOK);
  2087.     return FALSE;
  2088.   }
  2089.  
  2090. /* Calculate overlap.                                   */
  2091.   olap->x0 = (b1->x0 > b2->x0 ? b1->x0 : b2->x0);
  2092.   olap->x1 = (b1->x1 < b2->x1 ? b1->x1 : b2->x1);
  2093.   if (olap->x0 >= olap->x1) return FALSE;
  2094.  
  2095.   olap->y0 = (b1->y0 > b2->y0 ? b1->y0 : b2->y0);
  2096.   olap->y1 = (b1->y1 < b2->y1 ? b1->y1 : b2->y1);
  2097.   if (olap->y0 >= olap->y1) return FALSE;
  2098.  
  2099.   return TRUE;
  2100. }
  2101.  
  2102. /*------------------------------------------------------*/
  2103. /*  Function to check if a printer driver is installed  */
  2104. /*------------------------------------------------------*/
  2105. static BOOL pdriver_installed(void)
  2106. {
  2107. /* Returns TRUE if a printer driver is installed.       */
  2108. /* Displays an error message and returns FALSE if no    */
  2109. /* printer driver is installed (or other error occurs). */
  2110.  
  2111.   os_error pdriver_error = {         /* Define a new    */
  2112.     255,                             /* type of error.  */
  2113.     "No printer driver installed"};
  2114.   os_error *errptr;
  2115.   print_infostr printerinfo;
  2116.  
  2117. /* Attempt to get printer information from driver.      */
  2118.   errptr = print_info(&printerinfo);
  2119.  
  2120. /* All OK if no error.                                  */
  2121.   if (errptr == NULL) return TRUE;
  2122.  
  2123. /* If error is "No such SWI", PDriver is not installed. */
  2124.   if (errptr->errnum == 486)
  2125.     wimpt_reporterror(&pdriver_error, wimp_EOK);
  2126.   else
  2127.     wimpt_reporterror(errptr, wimp_EOK);
  2128.  
  2129.   return FALSE;
  2130. }
  2131.  
  2132.  
  2133. /**** Functions dealing with Selection of an Object *****/
  2134.  
  2135. /*------------------------------------------------------*/
  2136. /*              Function to cancel selection.           */
  2137. /*------------------------------------------------------*/
  2138. static void cancel_selection(void)
  2139. {
  2140.   /* Remove frame around old selection (if one is       */
  2141.   /* currently in window).                              */
  2142.   if (selection_exists)
  2143.   {
  2144.     if (horiz_data.selnum != NOWHERE)
  2145.       invalidate_framearea(horiz_handle, horiz_data);
  2146.     if (vert_data.selnum != NOWHERE)
  2147.       invalidate_framearea(vert_handle, vert_data);
  2148.   }
  2149.   /* Reset selection flag.                              */
  2150.   selection_exists = FALSE;
  2151.   /* Set owner number to invalid value.                 */
  2152.   sel_owner = NO_ONE;
  2153.  
  2154.   return;
  2155. }
  2156.  
  2157. /*------------------------------------------------------*/
  2158. /*     Function to display info on selected object.     */
  2159. /*------------------------------------------------------*/
  2160. static void sel_info(void)
  2161. {
  2162.   dbox d;                        /* Dialogue box handle.*/
  2163.   int i;                         /* Line counter.       */
  2164.   char timestring[8];            /* For times of phenom.*/
  2165.   char *linestart[INFOBOX_LINES];/* Array of pointers   */
  2166.                                  /* to start of each    */
  2167.                                  /* line of info text.  */
  2168.  
  2169.   /* Create dbox.                                       */
  2170.   d = dbox_new(SEL_INFO_NAME);
  2171.   if (d == NULL) return;
  2172.  
  2173. /* Build strings containing times of phenomena, and     */
  2174. /* display them in the dbox.                            */
  2175.   build_timestring(timestring,
  2176.              selection.rising, selection.rise_hour, selection.rise_min);
  2177.   dbox_setfield(d, selinfo_rise, timestring);
  2178.   build_timestring(timestring,
  2179.              selection.setting, selection.set_hour, selection.set_min);
  2180.   dbox_setfield(d, selinfo_set, timestring);
  2181.   build_timestring(timestring,
  2182.              selection.culminating, selection.cul_hour, selection.cul_min);
  2183.   dbox_setfield(d, selinfo_culmin, timestring);
  2184.  
  2185. /* Set up pointer to text buffer, and obtain info.      */
  2186.   infoptr = infobuf;
  2187.   module_fns[sel_owner].selectfn(sel_info_request);
  2188.  
  2189. /* Cut up returned text string into separate lines,     */
  2190. /* obtaining pointers to start of each line.            */
  2191.   cutup(linestart);
  2192.  
  2193. /* Fill in the text lines in the dbox.                  */
  2194.   for (i=0; i < INFOBOX_LINES; i++)
  2195.     dbox_setfield(d, selinfo_textline+i, linestart[i]);
  2196.  
  2197. /* Display dbox until user cancels or okays it.         */
  2198.   dbox_show(d);
  2199.   dbox_fillin(d);
  2200.  
  2201.   /* Delete dbox.                                       */
  2202.   dbox_dispose(&d);
  2203.  
  2204.   return;
  2205. }
  2206.  
  2207. /*------------------------------------------------------*/
  2208. /* Function to build a string containing the time of a  */
  2209. /*                      phenomenon.                     */
  2210. /*String pointed to by stringptr is assumed long enough.*/
  2211. /*------------------------------------------------------*/
  2212. static void build_timestring(char *stringptr, BOOL valid, int hour, int min)
  2213. {
  2214. /* If hour and min are not valid, return a suitable     */
  2215. /* string.                                              */
  2216.   if (!valid)
  2217.   {
  2218.     sprintf(stringptr, "--:--");
  2219.     return;
  2220.   }
  2221.  
  2222. /* Build time string in style of a 24-hour clock.       */
  2223.   sprintf(stringptr, "%02.2i:%02.2i", hour, min);
  2224.  
  2225.   return;
  2226. }
  2227.  
  2228.  
  2229. /*------------------------------------------------------*/
  2230. /*          Handle hits on 'View sel.' Sub-Menu         */
  2231. /*------------------------------------------------------*/
  2232.  
  2233. static void view_sub_menuproc(char *hit)
  2234. {
  2235.   REAL view_alt;  /* Altitude of object at time of      */
  2236.                   /* phenomenon.                        */
  2237.   REAL view_azim; /* Azimuth of object at time of       */
  2238.                   /* phenomenon.                        */
  2239.   int  best_dir;  /* Which of the standard directions   */
  2240.                   /* is best for viewing this azimuth.  */
  2241.   int selnum;     /* Window ID number of object.        */
  2242.  
  2243.   switch (hit[1])
  2244.   {
  2245.     case view_item_now:
  2246.       /* If the object is not in fact visible as billed,*/
  2247.       /* quit at once.  This should not happen.         */
  2248.       if (horiz_data.selnum == NOWHERE &&
  2249.            vert_data.selnum == NOWHERE) return;
  2250.       /* Find the altitude and azimuth of the object now*/
  2251.       if (horiz_data.selnum != NOWHERE)
  2252.       {
  2253.         selnum    = horiz_data.selnum;
  2254.         view_alt  = horiz_list[selnum].alt;
  2255.         view_azim = horiz_list[selnum].azim;
  2256.       }
  2257.       else
  2258.       {
  2259.         selnum    = vert_data.selnum;
  2260.         view_alt  = vert_list[selnum].alt;
  2261.         view_azim = vert_list[selnum].azim;
  2262.       }
  2263.       /* Set the direction of view to the best one for  */
  2264.       /* viewing the object (if it is currently         */
  2265.       /* some other direction).                         */
  2266.       best_dir = nearest_dir(view_azim);
  2267.       if (ob_data.dir != best_dir)
  2268.       {
  2269.         ob_data.dir = best_dir;
  2270.         ob_data.ang = direction[ob_data.dir];
  2271.         /*Recalc screen coords to reflect new direction.*/
  2272.         recalc_horiz_xy();
  2273.         recalc_vert_xy();
  2274.         /* Invalidate windows.                          */
  2275.         invalidate_all();
  2276.       }
  2277.       /* Arrange windows appropriately to display object*/
  2278.       arrange_windows(view_alt, view_azim);
  2279.  
  2280.       return;
  2281.  
  2282.     case view_item_rising:
  2283.       /* Change observer data so that the rising of the */
  2284.       /* selected object can be viewed.                 */
  2285.       ob_data.hour = selection.rise_hour;
  2286.       ob_data.min  = selection.rise_min;
  2287.       view_alt     = (REAL)0.0;
  2288.       view_azim    = selection.rise_azim;
  2289.       ob_data.dir  = nearest_dir(view_azim);
  2290.       break;
  2291.  
  2292.     case view_item_setting:
  2293.       /* Change observer data so that the setting of    */
  2294.       /* the selected object can be viewed.             */
  2295.       ob_data.hour = selection.set_hour;
  2296.       ob_data.min  = selection.set_min;
  2297.       view_alt     = (REAL)0.0;
  2298.       view_azim    = selection.set_azim;
  2299.       ob_data.dir  = nearest_dir(view_azim);
  2300.       break;
  2301.  
  2302.     case view_item_culminating:
  2303.       /* Change observer data so that the culminating   */
  2304.       /* of the selected object can be viewed.          */
  2305.       ob_data.hour = selection.cul_hour;
  2306.       ob_data.min  = selection.cul_min;
  2307.       view_alt     = selection.cul_alt;
  2308.       view_azim    = selection.cul_azim;
  2309.       ob_data.dir  = nearest_dir(view_azim);
  2310.       break;
  2311.  
  2312.     default:
  2313.       return;
  2314.   }
  2315.  
  2316.   /* Fill in derived fields of ob_data (ang, jul, etc)  */
  2317.   fillin_obfields(&ob_data);
  2318.  
  2319.   /* Rebuild the plotting lists for the new time of day.*/
  2320.   build_lists();
  2321.  
  2322.   /* Update Selection data to correspond to new lists.  */
  2323.   module_fns[sel_owner].selectfn(timeonly_recalculate);
  2324.  
  2325.   /* Record new id numbers of selected object.          */
  2326.   horiz_data.selnum = selection.horiz_id;
  2327.   vert_data.selnum  = selection.vert_id;
  2328.  
  2329.   /* Ensure size of Selection frame is valid.           */
  2330.   if (horiz_data.selnum != NOWHERE)
  2331.     calculate_frame(&horiz_data);
  2332.   if (vert_data.selnum != NOWHERE)
  2333.     calculate_frame(&vert_data);
  2334.  
  2335.   /* Invalidate windows.                                */
  2336.   invalidate_all();
  2337.  
  2338.   /* Arrange windows appropriately to display object.   */
  2339.   arrange_windows(view_alt, view_azim);
  2340.  
  2341.   return;
  2342. }
  2343.  
  2344. /*------------------------------------------------------*/
  2345. /* Function to find which of the standard directions of */
  2346. /*      view is the nearest to the given azimuth.       */
  2347. /*------------------------------------------------------*/
  2348. static int nearest_dir(REAL azim)
  2349. {
  2350.   REAL min_error = (REAL)2.0 * PI;
  2351.   REAL error;
  2352.   int i;
  2353.   int nearest = 0;
  2354.  
  2355.   for (i=0; i<NDIR; i++)
  2356.   {
  2357.     error = direction[i] - azim;
  2358.     error = (REAL)fabs((double)error);
  2359.     if (error < min_error)
  2360.     {
  2361.       min_error = error;
  2362.       nearest = i;
  2363.     }
  2364.   }
  2365.   return nearest;
  2366. }
  2367.  
  2368. /*------------------------------------------------------*/
  2369. /*  Function to arrange the windows to bring the object */
  2370. /*  at the given altitude and azimuth onto the screen.  */
  2371. /*------------------------------------------------------*/
  2372. static void arrange_windows(REAL alt, REAL az)
  2373. {
  2374. /* If the given altitude is greater than the maximum    */
  2375. /* altitude which appears in the Horiz window, use the  */
  2376. /* Vert window.                                         */
  2377.   if (alt >= alt_max)
  2378.   {
  2379.     arrange_vert(alt, az);
  2380.     return;
  2381.   }
  2382.  
  2383. /* Otherwise use the Horiz window.                      */
  2384.   arrange_horiz(alt, az);
  2385.  
  2386.   return;
  2387. }
  2388.  
  2389. /*                                                      */
  2390. /* Function to arrange Horiz window appropriately to    */
  2391. /* display the object at the given alt and az.          */
  2392. /*                                                      */
  2393. static void arrange_horiz(REAL alt, REAL az)
  2394. {
  2395. /* Assume Horiz window is at full height (or was when   */
  2396. /* it was last open).                                   */
  2397. /* Ensure window is open and on top, and scroll it to   */
  2398. /* bring the given azimuth to the centre of the visible */
  2399. /* portion.  Ignore altitude.                           */
  2400.  
  2401.   wimp_wstate winstate;
  2402.   int x_coord;          /* x coord corresponding to az. */
  2403.   int viswidth;         /* Width of visible part of win.*/
  2404.   int x_scroll;         /* Scroll offset required.      */
  2405.  
  2406. /* Get current state of Horiz window.                   */
  2407.   wimpt_noerr(wimp_get_wind_state(horiz_handle, &winstate));
  2408.  
  2409. /* Calculate current width of visible portion.          */
  2410.   viswidth = winstate.o.box.x1 - winstate.o.box.x0;
  2411.  
  2412. /* Calculate x coord corresponding to given azimuth.    */
  2413.   x_coord = x_h(az);
  2414.  
  2415. /* Calculate scroll offset needed to put x_coord into   */
  2416. /* centre of visible portion.                           */
  2417.   x_scroll = x_coord - (viswidth/2);
  2418.  
  2419. /* Make sure x_scroll is valid.                         */
  2420.   if (x_scroll < horiz_size.x0)
  2421.     x_scroll = horiz_size.x0;
  2422.   if (x_scroll + viswidth > horiz_size.x1)
  2423.     x_scroll = horiz_size.x1 - viswidth;
  2424.  
  2425. /* Call Wimp_OpenWindow with new value of x_scroll.     */
  2426. /* Also, ensure Horiz window is on top of stack.        */
  2427.   winstate.o.x      =  x_scroll;
  2428.   winstate.o.behind = -1;
  2429.   wimpt_noerr(wimp_open_wind(&winstate.o));
  2430.  
  2431.   return;
  2432. }
  2433.  
  2434. /*                                                      */
  2435. /* Function to arrange Vert window appropriately to     */
  2436. /* display object at given alt and az.                  */
  2437. /*                                                      */
  2438. static void arrange_vert(REAL alt, REAL az)
  2439. {
  2440. /* Assume Vert window is at full size (or was when it   */
  2441. /* was last open).                                      */
  2442. /* Ensure window is open and on top of stack.           */
  2443.  
  2444.   wimp_wstate winstate;
  2445.  
  2446. /* Get current state of Vert window.                    */
  2447.   wimpt_noerr(wimp_get_wind_state(vert_handle, &winstate));
  2448.  
  2449. /* Call Wimp_OpenWindow, ensuring Vert window is on top */
  2450. /* of stack.                                            */
  2451.   winstate.o.behind = -1;
  2452.   wimpt_noerr(wimp_open_wind(&winstate.o));
  2453.  
  2454.   return;
  2455. }
  2456.  
  2457. /*------------------------------------------------------*/
  2458. /*  Function to invoke Select function appropriate to   */
  2459. /*               hit on Select sub-menu                 */
  2460. /*------------------------------------------------------*/
  2461. static void invoke_selectfns(char *hit)
  2462. {
  2463.   selectfntype *appropriate_fn;
  2464.   int module_no;    /* For module no. of owner of this  */
  2465.                     /* menu entry.                      */
  2466.  
  2467. /* For unknown option, do nothing                       */
  2468.   if (hit[1] < 1 ) return;
  2469.   if (hit[1] > select_count) return;
  2470.  
  2471. /* Find which module owns this menu entry.              */
  2472.   module_no = sel_index[hit[1]];
  2473.  
  2474. /* Give modules access to list of submenu hits.         */
  2475.   module_submenu_hits = hit+2;
  2476.  
  2477. /* Find appropriate Select function.                    */
  2478.   appropriate_fn = module_fns[module_no].selectfn;
  2479.  
  2480. /* Set looping flag to FALSE. Might be changed by module*/
  2481.   dbox_persisting = FALSE;
  2482.  
  2483. /* Enclose module call in loop. End when flag is FALSE. */
  2484.   do
  2485.   {
  2486.  
  2487.   /* Invoke appropriate Select function.                */
  2488.     appropriate_fn(new_selection);
  2489.  
  2490.   /* Establish new selection, if dialogue with user was */
  2491.   /* successful.                                        */
  2492.     if (selection.selected_OK)
  2493.       establish_new_selection(module_no);
  2494.  
  2495. /* End of loop for persisting dbox.                     */
  2496.   }
  2497.   while (dbox_persisting);
  2498.  
  2499. /* List of submenu hits no longer valid.                */
  2500.   module_submenu_hits = NULL;
  2501.  
  2502.   return;
  2503. }
  2504.  
  2505. /*------------------------------------------------------*/
  2506. /*     Function to establish a new selected object.     */
  2507. /*------------------------------------------------------*/
  2508. static void establish_new_selection(int new_owner)
  2509. {
  2510.  
  2511. /* Remove frame around old selection (if one is         */
  2512. /* currently in window).                                */
  2513.   if (selection_exists)
  2514.   {
  2515.     if (horiz_data.selnum != NOWHERE)
  2516.       invalidate_framearea(horiz_handle, horiz_data);
  2517.     if (vert_data.selnum != NOWHERE)
  2518.       invalidate_framearea(vert_handle, vert_data);
  2519.   }
  2520.  
  2521. /* Flag the existence of new selection.                 */
  2522.   selection_exists = TRUE;
  2523.  
  2524. /* Record the new owner of the selection.               */
  2525.   sel_owner = new_owner;
  2526.  
  2527. /* Record window number of new selection. Number =      */
  2528. /* NOWHERE if selection is not in window.               */
  2529.   horiz_data.selnum = selection.horiz_id;
  2530.   vert_data.selnum  = selection.vert_id;
  2531.  
  2532. /* Establish new frame (if selected object is in window)*/
  2533.   if (horiz_data.selnum != NOWHERE)
  2534.     establish_new_frame(horiz_handle, &horiz_data);
  2535.   if (vert_data.selnum != NOWHERE)
  2536.     establish_new_frame(vert_handle, &vert_data);
  2537.  
  2538.   return;
  2539. }
  2540.  
  2541. /*------------------------------------------------------*/
  2542. /*    Function to invalidate the part of the window     */
  2543. /*   containing the frame around the selected object.   */
  2544. /*------------------------------------------------------*/
  2545. static void invalidate_framearea(wimp_w whandle, sv_windata wdat)
  2546. {
  2547.   wimp_redrawstr r;
  2548.  
  2549. /* Point to selected object:                            */
  2550.   plotobj *selptr = (wdat.listptr + wdat.selnum);
  2551.  
  2552.   /* Get x and y coords of selected object.             */
  2553.   int x = selptr->wind_x;
  2554.   int y = selptr->wind_y;
  2555.  
  2556.   /* Shift frame's bounding box to coords of object.    */
  2557.   /* Put result into redrawstr.                         */
  2558.   coords_offsetbox(&wdat.sel_bounds, x, y, &r.box);
  2559.  
  2560.   /* Fill in window handle.                             */
  2561.   r.w = whandle;
  2562.  
  2563.   /* Invalidate the part of the window containing the   */
  2564.   /* frame.                                             */
  2565.   wimpt_noerr(wimp_force_redraw(&r));
  2566.  
  2567.   return;
  2568. }
  2569.  
  2570.  
  2571. /*------------------------------------------------------*/
  2572. /*  Function to establish frame around new selection.   */
  2573. /*------------------------------------------------------*/
  2574. static void establish_new_frame(wimp_w whandle, sv_windata *wd)
  2575. {
  2576.  
  2577.   /* Calculate coords of frame and bounding box for     */
  2578.   /* frame, and write them into window's data block.    */
  2579.   calculate_frame(wd);
  2580.  
  2581.   /* Invalidate the part of the window which is to      */
  2582.   /* contain the new frame.                             */
  2583.   invalidate_framearea(whandle, *wd);
  2584.  
  2585.   return;
  2586. }
  2587.  
  2588. /*------------------------------------------------------*/
  2589. /*     Function to calculate coords of frame around     */
  2590. /*    selection, and coords of bounding box of frame,   */
  2591. /*     and write them into the window's data block.     */
  2592. /*------------------------------------------------------*/
  2593. static void calculate_frame(sv_windata *wd)
  2594. {
  2595.  
  2596.   wimp_box size;
  2597.  
  2598.   /* Read size of new selected object.                  */
  2599.   size = (wd->listptr + wd->selnum)->size;
  2600.  
  2601.   /* Construct a frame larger than size and centered    */
  2602.   /* on 0,0.                                            */
  2603.   wd->sel_frame.w = 2*(size.x1 - size.x0);
  2604.   wd->sel_frame.h = 2*(size.y1 - size.y0);
  2605.   wd->sel_frame.x = -(wd->sel_frame.w)/2;
  2606.   wd->sel_frame.y = -(wd->sel_frame.h)/2;
  2607.  
  2608.   /* Set up bounding box which encloses frame.          */
  2609.   wd->sel_bounds.x0 = wd->sel_frame.x - 4;
  2610.   wd->sel_bounds.y0 = wd->sel_frame.y - 4;
  2611.   wd->sel_bounds.x1 = wd->sel_frame.x +\
  2612.                       wd->sel_frame.w + 6;
  2613.   wd->sel_bounds.y1 = wd->sel_frame.y +\
  2614.                       wd->sel_frame.h + 6;
  2615.  
  2616.   return;
  2617. }
  2618.  
  2619. /*********** 'Display' Options for Modules **************/
  2620. /*------------------------------------------------------*/
  2621. /*    Function to invoke display function appropriate   */
  2622. /*             to hit on Display sub-menu.              */
  2623. /*------------------------------------------------------*/
  2624. static void invoke_displayfns(char *hit)
  2625. {
  2626.   BOOL w_invalid;/* TRUE if windows become invalid.     */
  2627.   int module_no; /* For module no. of owner of this     */
  2628.                  /* menu entry.                         */
  2629.   displayfntype *appropriate_fn;
  2630.  
  2631. /* For unknown option, do nothing                       */
  2632.   if (hit[1] < 1 ) return;
  2633.   if (hit[1] > display_count) return;
  2634.  
  2635. /* Give modules access to list of submenu hits.         */
  2636.   module_submenu_hits = hit+2;
  2637.  
  2638. /* Find owner of this menu entry.                       */
  2639.   module_no = disp_index[hit[1]];
  2640.  
  2641. /* Find appropriate Display function.                   */
  2642.   appropriate_fn = module_fns[module_no].dispfn;
  2643.  
  2644. /* Set looping flag to FALSE. Might be changed by module*/
  2645.   dbox_persisting = FALSE;
  2646.  
  2647. /* Enclose module call in loop. End when flag is FALSE. */
  2648.   do
  2649.   {
  2650.  
  2651.   /* Set rebuilding flag to FALSE. Module can change it.*/
  2652.     rebuild_request = FALSE;
  2653.   /* Invoke appropriate display function.               */
  2654.     w_invalid = appropriate_fn(&enabled[module_no]);
  2655.  
  2656.   /* Set tick on menu entry accordingly.                */
  2657.     menu_setflags(display_sub_menu, hit[1], enabled[module_no], 0);
  2658.  
  2659.   /* Make sure a Selection cannot be made on a disabled */
  2660.   /* module. If module has a Select entry, set the fade */
  2661.   /* status of that entry according to the value of the */
  2662.   /* tick flag just returned.                           */
  2663.     if (module_index[module_no] > 0)
  2664.       menu_setflags(sel_sub_menu, module_index[module_no],\
  2665.                     0, !enabled[module_no]);
  2666.  
  2667.   /* If module is now disabled, and it was the owner of */
  2668.   /* the selection, cancel the selection.               */
  2669.     if (module_no==sel_owner && !enabled[module_no])
  2670.       cancel_selection();
  2671.  
  2672.   /* Declare windows invalid, if necessary.             */
  2673.     if (w_invalid || rebuild_request) invalidate_all();
  2674.  
  2675.   /* Rebuild plotting lists, if necessary.              */
  2676.     if (rebuild_request) display_rebuild();
  2677.  
  2678.  
  2679. /* End of loop for persisting dbox.                     */
  2680.   }
  2681.   while (dbox_persisting);
  2682.  
  2683. /* List of submenu hits no longer valid.                */
  2684.   module_submenu_hits = NULL;
  2685.  
  2686.   return;
  2687. }
  2688.  
  2689. /*------------------------------------------------------*/
  2690. /*  Function to rebuild plotting lists, at the request  */
  2691. /*               of a Display function.                 */
  2692. /*------------------------------------------------------*/
  2693. static void display_rebuild(void)
  2694. {
  2695.  
  2696.   build_lists();
  2697.  
  2698. /* Nothing more to do if there is no selected object.   */
  2699.   if (!selection_exists) return;
  2700.  
  2701. /* Call selection function for owner of object.  Reason */
  2702. /* 'timeonly_recalculate' will do.                      */
  2703.   module_fns[sel_owner].selectfn(timeonly_recalculate);
  2704.  
  2705. /* Record new id numbers of selected object.            */
  2706.   horiz_data.selnum = selection.horiz_id;
  2707.   vert_data.selnum  = selection.vert_id;
  2708.  
  2709. /* If object is on view, make sure coords of frame and  */
  2710. /* bounding box are valid.                              */
  2711.   if (horiz_data.selnum != NOWHERE)
  2712.     calculate_frame(&horiz_data);
  2713.   if (vert_data.selnum  != NOWHERE)
  2714.     calculate_frame(&vert_data);
  2715.  
  2716.   return;
  2717. }
  2718.  
  2719.  
  2720. /********************************************************/
  2721. /*                   Info Box Handler                   */
  2722. /********************************************************/
  2723.  
  2724. static void sv_info_about_program(void)
  2725. {
  2726.   dbox d;      /* Dialogue box handle */
  2727.   char versionstring[256];
  2728.  
  2729.   if (d = dbox_new("progInfo"), d != NULL)
  2730.   {
  2731.     sprintf(versionstring, "%s (%s) ",VERSION,__DATE__);
  2732.     dbox_setfield(d, sv_info_field, versionstring);
  2733.  
  2734.     dbox_show(d);      /* display it  */
  2735.     dbox_fillin(d);    /* maintain it */
  2736.     dbox_dispose(&d);  /* remove it   */
  2737.   }
  2738. }
  2739.  
  2740.  
  2741. /********************************************************/
  2742. /*           Functions for Observer Details             */
  2743. /********************************************************/
  2744.  
  2745. /*------------------------------------------------------*/
  2746. /*       Function to obtain new Observer Details        */
  2747. /*         and update windows accordingly.              */
  2748. /*------------------------------------------------------*/
  2749. static void observer(void)
  2750. {
  2751.   int response;        /* Which action button has just  */
  2752.                        /* been been pressed.            */
  2753.   int target;          /* Value that the Day field      */
  2754.                        /* "wants" to have.              */
  2755.   BOOL keep_open;      /* TRUE if the dbox is to remain */
  2756.                        /* open after the present button */
  2757.                        /* has been processed.           */
  2758.   int year;            /* For changing century.         */
  2759.   char buf[10];        /* For changing N/S and E/W.     */
  2760.  
  2761. /* Load dbox with the settings currently in force.      */
  2762.   data_to_box(ob_data);
  2763.  
  2764. /* Put Observer Box on screen.                          */
  2765.   dbox_show(d_ob);
  2766.  
  2767. /* Set value of target.                                 */
  2768.   target = dbox_getnumeric(d_ob, dayf);
  2769.  
  2770.  
  2771. /* Loop until Select on 'OK', or 'close' request.       */
  2772.  
  2773.   do
  2774.   {
  2775. /* Get number of action button pressed by the user.     */
  2776.     response = dbox_fillin(d_ob);
  2777.  
  2778. /* Take appropriate action.                             */
  2779.     switch (response) {
  2780.  
  2781.       case ob_close_rq:
  2782.       /* Do nothing.  Data on display may be invalid,   */
  2783.       /* but box is being closed anyway.                */
  2784.         keep_open = FALSE;
  2785.         break;
  2786.  
  2787.       case ob_cancel_butt:
  2788.       /* Put the current data back on display.          */
  2789.         data_to_box(ob_data);
  2790.       /* Reset the value of target.                     */
  2791.         target = dbox_getnumeric(d_ob, dayf);
  2792.       /* Keep dbox open if user has so requested.       */
  2793.         keep_open = dbox_persist();
  2794.         break;
  2795.  
  2796.       case ob_ok_butt:
  2797.       /* Call handler function for clicks on OK button. */
  2798.         ob_ok_click();
  2799.       /* Reset the value of target.                     */
  2800.         target = dbox_getnumeric(d_ob, dayf);
  2801.       /* Keep dbox open if user has so requested.       */
  2802.         keep_open = dbox_persist();
  2803.         break;
  2804.  
  2805.       case set_butt:
  2806.       /* Set Default to the data now on display in      */
  2807.       /* the dbox.  (This is not necessarily the same   */
  2808.       /* as the settings currently in force.)           */
  2809.       /* If error, report it & continue.                */
  2810.         if (!save_ob_data())
  2811.         {
  2812.           werr(NON_FATAL, "Can't set defaults");
  2813.           keep_open = TRUE;
  2814.         }
  2815.         else
  2816.       /* Reset the value of target, and                 */
  2817.       /* Keep dbox open if user has so requested.       */
  2818.         {
  2819.           target = dbox_getnumeric(d_ob, dayf);
  2820.           keep_open = dbox_persist();
  2821.         }
  2822.         break;
  2823.  
  2824.       case show_butt:
  2825.       /* Load the default settings into the dbox, for   */
  2826.       /* user to inspect, alter, or whatever.           */
  2827.       /* If error, report it & continue.                */
  2828.         if (!load_ob_data())
  2829.           werr(NON_FATAL, "Can't load defaults");
  2830.         else
  2831.       /* Reset the value of target.                     */
  2832.           target = dbox_getnumeric(d_ob, dayf);
  2833.       /* Keep dbox open.                                */
  2834.         keep_open = TRUE;
  2835.         break;
  2836.  
  2837.       case sys_date_butt:
  2838.       /* Write the date from the system clock into      */
  2839.       /* the dbox.                                      */
  2840.         load_sys_date();
  2841.       /* Reset the value of target.                     */
  2842.         target = dbox_getnumeric(d_ob, dayf);
  2843.       /* Keep dbox open.                                */
  2844.         keep_open = TRUE;
  2845.         break;
  2846.  
  2847.       case cent_butt:
  2848.       /* Toggle between 20th and 21st centuries.        */
  2849.         year = dbox_getnumeric(d_ob, yrf);
  2850.         year = (year >= 2000 ? year-100 : year+100);
  2851.         dbox_setnumeric(d_ob, yrf, year);
  2852.       /* Keep dbox open.                                */
  2853.         keep_open = TRUE;
  2854.         break;
  2855.  
  2856.       case ns_butt: case n_or_s:
  2857.       /* Toggle between N and S latitude.               */
  2858.         dbox_getfield(d_ob, nsf, buf, 10);
  2859.         buf[0] = (buf[0]=='N'? 'S' : 'N');
  2860.         dbox_setfield(d_ob, nsf, buf);
  2861.       /* Keep dbox open.                                */
  2862.         keep_open = TRUE;
  2863.         break;
  2864.  
  2865.       case ew_butt: case e_or_w:
  2866.       /* Toggle between E and W longitude.              */
  2867.         dbox_getfield(d_ob, ewf, buf, 10);
  2868.         buf[0] = (buf[0]=='W'? 'E' : 'W');
  2869.         dbox_setfield(d_ob, ewf, buf);
  2870.       /* Keep dbox open.                                */
  2871.         keep_open = TRUE;
  2872.         break;
  2873.  
  2874.       default:
  2875.       /* Must be one of the date / time changing butts. */
  2876.       /* Call function to handle clicks on arrows.      */
  2877.         ob_arrows(response, &target);
  2878.       /* Keep dbox open.                                */
  2879.         keep_open = TRUE;
  2880.         break;
  2881.     }
  2882.  
  2883.   } while (keep_open);
  2884.  
  2885. /* Remove dbox from screen    */
  2886.   dbox_hide(d_ob);
  2887.  
  2888.   return;
  2889. }
  2890.  
  2891.  
  2892. /*------------------------------------------------------*/
  2893. /*       Handle clicks on data-changing buttons         */
  2894. /*                    in Observer Box                   */
  2895. /*------------------------------------------------------*/
  2896. static void ob_arrows(int response, int *targptr)
  2897. {
  2898.   char numstring[10];/* For number / string conversions */
  2899.  
  2900.   int lati;
  2901.   int latd;
  2902.   int lat10;
  2903.   int longi;
  2904.   int longd;
  2905.   int long10;
  2906.   int day;
  2907.   int month;         
  2908.   int year;          
  2909.   int lastday;       /* Last day of the month.          */
  2910.   int hour;
  2911.   int min;
  2912.   float offset;
  2913.  
  2914.   switch (response) {
  2915.  
  2916.     /* Latitude (integer part) */
  2917.     case lai_tu: case lai_td: case lai_uu: case lai_ud:
  2918.       lati = dbox_getnumeric(d_ob, laif);
  2919.       latd = dbox_getnumeric(d_ob, ladf);
  2920.       lati += addon[lai_ud-response];
  2921.       lat10 = 10*lati + latd;
  2922.       if (lat10 < 900 && lat10 >= 0)
  2923.       {
  2924.       /* Set integer field of latitude display.         */
  2925.         sprintf(numstring, "%02.2i", lat10/10);
  2926.         dbox_setfield(d_ob, laif, numstring);
  2927.       }
  2928.       break;
  2929.  
  2930.     /* Latitude (decimal part) */
  2931.     case lad_uu: case lad_ud:
  2932.       lati = dbox_getnumeric(d_ob, laif);
  2933.       latd = dbox_getnumeric(d_ob, ladf);
  2934.       latd += addon[lad_ud-response];
  2935.       lat10 = 10*lati + latd;
  2936.       if (lat10 < 900 && lat10 >= 0)
  2937.       {
  2938.       /* Set both fields of latitude display.           */
  2939.         sprintf(numstring, "%02.2i", lat10/10);
  2940.         dbox_setfield(d_ob, laif, numstring);
  2941.         dbox_setnumeric(d_ob, ladf, lat10%10);
  2942.       }
  2943.       break;
  2944.  
  2945.     /* Longitude (integer part) */
  2946.     case loi_hu: case loi_hd: case loi_tu: 
  2947.     case loi_td: case loi_uu: case loi_ud:
  2948.       longi  = dbox_getnumeric(d_ob, loif);
  2949.       longd  = dbox_getnumeric(d_ob, lodf);
  2950.       longi += addon[loi_ud-response];
  2951.       long10 = 10*longi + longd;
  2952.       if (long10 <= 1800 && long10 >= 0)
  2953.       {
  2954.       /* Set integer field of longitude display.        */
  2955.         sprintf(numstring, "%03.3i", long10/10);
  2956.         dbox_setfield(d_ob, loif, numstring);
  2957.       }
  2958.       break;
  2959.  
  2960.     /* Longitude (decimal part) */
  2961.     case lod_uu: case lod_ud:
  2962.       longi  = dbox_getnumeric(d_ob, loif);
  2963.       longd  = dbox_getnumeric(d_ob, lodf);
  2964.       longd += addon[lod_ud-response];
  2965.       long10 = 10*longi + longd;
  2966.       if (long10 <= 1800 && long10 >= 0)
  2967.       {
  2968.       /* Set both fields of longitude display.          */
  2969.         sprintf(numstring, "%03.3i", long10/10);
  2970.         dbox_setfield(d_ob, loif, numstring);
  2971.         dbox_setnumeric(d_ob, lodf, long10%10);
  2972.       }
  2973.       break;
  2974.  
  2975.     /* Years   */
  2976.     case yr_tu:  case yr_td:  case yr_uu:  case yr_ud:
  2977.       year = dbox_getnumeric(d_ob, yrf);
  2978.       year += addon[yr_ud-response];
  2979.       if (year <= 2099 && year >= 1900)
  2980.       {
  2981.       /* Set year field.  Make day field as close as    */
  2982.       /* possible to its target, while keeping it valid.*/
  2983.         dbox_setnumeric(d_ob, yrf, year);
  2984.         month = dbox_getnumeric(d_ob, monf);
  2985.         lastday = daysin[leap(year)][month];
  2986.         day = (*targptr > lastday ? lastday : *targptr);
  2987.         sprintf(numstring, "%02.2i", day);
  2988.         dbox_setfield(d_ob, dayf, numstring);
  2989.       }
  2990.       break;
  2991.  
  2992.     /* Months  */
  2993.     case mon_tu: case mon_td: case mon_uu: case mon_ud:
  2994.       year   = dbox_getnumeric(d_ob, yrf );
  2995.       month  = dbox_getnumeric(d_ob, monf);
  2996.       month += addon[mon_ud-response];
  2997.       if (month <= 12 && month > 0)
  2998.       {
  2999.       /* Set month field.  Make day field as close as   */
  3000.       /* possible to its target, while keeping it valid.*/
  3001.         sprintf(numstring, "%02.2i", month);
  3002.         dbox_setfield(d_ob, monf, numstring);
  3003.         year = dbox_getnumeric(d_ob, yrf );
  3004.         lastday = daysin[leap(year)][month];
  3005.         day = (*targptr > lastday ? lastday : *targptr);
  3006.         sprintf(numstring, "%02.2i", day);
  3007.         dbox_setfield(d_ob, dayf, numstring);
  3008.       }
  3009.       break;
  3010.  
  3011.     /* Days    */
  3012.     case day_tu: case day_td: case day_uu: case day_ud:
  3013.       year = dbox_getnumeric(d_ob, yrf );
  3014.       month= dbox_getnumeric(d_ob, monf);
  3015.       day  = dbox_getnumeric(d_ob, dayf);
  3016.       day += addon[day_ud-response];
  3017.       lastday = daysin[leap(year)][month];
  3018.       if (day <= lastday && day > 0)
  3019.       {
  3020.       /* Set day field and change value of target.      */
  3021.         sprintf(numstring, "%02.2i", day);
  3022.         dbox_setfield(d_ob, dayf, numstring);
  3023.         *targptr = day;
  3024.       }
  3025.       break;
  3026.  
  3027.     /* Hours   */
  3028.     case hr_tu:  case hr_td:  case hr_uu:  case hr_ud:
  3029.       hour  = dbox_getnumeric(d_ob, hrf);
  3030.       hour += addon[hr_ud-response];
  3031.       if (hour <= 23 && hour >= 0)
  3032.       {
  3033.         sprintf(numstring, "%02.2i", hour);
  3034.         dbox_setfield(d_ob, hrf, numstring);
  3035.       }
  3036.       break;
  3037.  
  3038.     /* Minutes */
  3039.     case min_tu: case min_td: case min_uu: case min_ud:
  3040.       min = dbox_getnumeric(d_ob, minf);
  3041.       min += addon[min_ud-response];
  3042.       if (min <= 59 && min >= 0)
  3043.       {
  3044.         sprintf(numstring, "%02.2i", min);
  3045.         dbox_setfield(d_ob, minf, numstring);
  3046.       }
  3047.       break;
  3048.  
  3049.     /* Time offset */
  3050.     case off_uu: case off_ud: case off_xu: case off_xd:
  3051.       dbox_getfield(d_ob, offf, numstring, 10);
  3052.       sscanf(numstring, "%f", &offset);
  3053.       offset += offset_incr[off_xd - response];
  3054.       if (offset >= -15.0f && offset <= 15.0f)
  3055.       {
  3056.         sprintf(numstring, "%+05.1f", (double)offset);
  3057.         dbox_setfield(d_ob, offf, numstring);
  3058.       }
  3059.       break;
  3060.  
  3061.     default:
  3062.       break;
  3063.  
  3064.   }
  3065.   return;
  3066. }
  3067.  
  3068.  
  3069. /*------------------------------------------------------*/
  3070. /*          Handle clicks on OK in Observer Box         */
  3071. /*------------------------------------------------------*/
  3072. static void ob_ok_click(void)
  3073. {
  3074.   observerstr new_data; /* New data read from dbox.     */
  3075.  
  3076.   BOOL dir_changed = FALSE;/* Becomes TRUE if user      */
  3077.                            /* changes direction of view.*/
  3078.   BOOL dat_changed = FALSE;/* Becomes TRUE if user      */
  3079.                            /* changes date field.       */
  3080.   BOOL tim_changed = FALSE;/* Becomes TRUE if user      */
  3081.                            /* changes time field.       */
  3082.   BOOL off_changed = FALSE;/* Becomes TRUE if user      */
  3083.                            /* changes time offset.      */
  3084.   BOOL loc_changed = FALSE;/* Becomes TRUE if user      */
  3085.                            /* changes location.         */
  3086.   BOOL dir_only    = FALSE;/* Becomes TRUE if user      */
  3087.                            /* changes just the direction*/
  3088.   BOOL tim_dir_only= TRUE; /* Becomes FALSE if user     */
  3089.                            /* changes dat, loc or offset*/
  3090.   BOOL any_changed = FALSE;/* Becomes TRUE if user      */
  3091.                            /* changes any of the data.  */
  3092.  
  3093. /* Read new data from dbox                              */
  3094.   data_from_box(&new_data);
  3095.  
  3096. /* Give special treatment if this is the first time.    */
  3097. if (!observer_verified)
  3098.   {
  3099.     /* Update observer data to match data read from box.*/
  3100.     ob_data = new_data;
  3101.  
  3102.     /* Flag observer data as verified.                  */
  3103.     observer_verified = TRUE;
  3104.  
  3105.     /* Build plotting lists.                            */
  3106.     build_lists();
  3107.  
  3108.     return;
  3109.   }
  3110.  
  3111. /* Normal Processing.                                   */
  3112.  
  3113. /* Examine new data to find out what's changed.         */
  3114. /* Note 'date' and 'time' here refer to the values app- */
  3115. /* earing in the dbox.  Need to look at 'offset' field  */
  3116. /* as well in order to detect changes.                  */
  3117.   off_changed = (fabs((double)new_data.offset - (double)ob_data.offset)
  3118.                  >= 0.1);
  3119.   dir_changed = (new_data.dir != ob_data.dir);
  3120.   dat_changed = (new_data.day   != ob_data.day   ||
  3121.                  new_data.month != ob_data.month ||
  3122.                  new_data.year  != ob_data.year);
  3123.   tim_changed = (new_data.hour  != ob_data.hour  ||
  3124.                  new_data.min   != ob_data.min);
  3125.   loc_changed = (new_data.lat10  != ob_data.lat10  ||
  3126.                  new_data.isn    != ob_data.isn    ||
  3127.                  new_data.long10 != ob_data.long10 ||
  3128.                  new_data.isw    != ob_data.isw);
  3129.   dir_only    =  dir_changed && !dat_changed && \
  3130.                 !tim_changed && !off_changed && !loc_changed;
  3131.   tim_dir_only= !dat_changed && !off_changed && !loc_changed;
  3132.   any_changed =  dir_changed || dat_changed || \
  3133.                  tim_changed || loc_changed || off_changed;
  3134.  
  3135.  
  3136. /* Do nothing if no changes have been made.             */
  3137.   if (!any_changed)
  3138.   return;
  3139.  
  3140. /* Change Observer Details to match new data.           */
  3141.   ob_data = new_data;
  3142.  
  3143. /* Invalidate the windows.                              */
  3144.   invalidate_all();
  3145.  
  3146. /* If the direction alone has changed, just re-calculate*/
  3147. /* the screen coords and then return.                   */
  3148.   if (dir_only)
  3149.   {
  3150.     recalc_horiz_xy();
  3151.     recalc_vert_xy();
  3152.     return;
  3153.   }
  3154. /*
  3155.  * The date or time have changed, so rebuild the
  3156.  * plotting lists, and re-calculate when the
  3157.  * selected object (if any) is visible.
  3158.  */
  3159.   build_lists();
  3160.  
  3161.   if (selection_exists)
  3162.   {
  3163.     /* Set special reason code if nothing but the time  */
  3164.     /* (and possibly the direction as well - this is of */
  3165.     /* no importance) has changed.                      */
  3166.     int reason = (tim_dir_only? \
  3167.         timeonly_recalculate : recalculate_data);
  3168.  
  3169.     /* Call Selection function for owner of object.     */
  3170.     module_fns[sel_owner].selectfn(reason);
  3171.     /* Record new id numbers of selected object.        */
  3172.     horiz_data.selnum = selection.horiz_id;
  3173.     vert_data.selnum  = selection.vert_id;
  3174.     /* If object is on view, ensure coords of frame and */
  3175.     /* bounding box of frame are valid.                 */
  3176.     if (horiz_data.selnum != NOWHERE)
  3177.       calculate_frame(&horiz_data);
  3178.     if (vert_data.selnum != NOWHERE)
  3179.       calculate_frame(&vert_data);
  3180.   }
  3181.  
  3182.   return;
  3183. }
  3184.  
  3185. /*------------------------------------------------------*/
  3186. /*             Fn to build plotting lists.              */
  3187. /*------------------------------------------------------*/
  3188. static void build_lists(void)
  3189. {
  3190.   int i;
  3191.   buildfntype *bfn;
  3192.  
  3193. /* Turn hour glass on.                                  */
  3194.   visdelay_begin();
  3195.  
  3196.   /* Reset number of objects to zero.                   */
  3197.   horiz_data.number = 0;
  3198.   vert_data.number  = 0;
  3199.  
  3200.   for (i=0; i < module_count; i++)
  3201.   {
  3202.      bfn = module_fns[i].buildfn;
  3203.      if (bfn != NULL) bfn();
  3204.   }
  3205.  
  3206. /* Turn hour glass off.                                 */
  3207.   visdelay_end();
  3208.  
  3209.   return;
  3210. }
  3211.  
  3212. /*------------------------------------------------------*/
  3213. /*   Fn to declare all parts of both windows invalid    */
  3214. /*------------------------------------------------------*/
  3215. static void invalidate_all(void)
  3216. {
  3217.   wimp_redrawstr r;
  3218.  
  3219.   r.w   = horiz_handle;
  3220.   r.box = horiz_size;
  3221.   wimpt_noerr(wimp_force_redraw(&r));
  3222.   r.w   = vert_handle;
  3223.   r.box = vert_size;
  3224.   wimpt_noerr(wimp_force_redraw(&r));
  3225.  
  3226.   return;
  3227. }
  3228.  
  3229. /*------------------------------------------------------*/
  3230. /*      Fn to write data into Observer Details box      */
  3231. /*------------------------------------------------------*/
  3232. static void data_to_box(observerstr data)
  3233. {
  3234.   int i;
  3235.   char numstring[10];
  3236.  
  3237. /* Set Latitude field (integer part, then decimal).     */
  3238.   sprintf(numstring, "%02.2i", data.lat10/10);
  3239.   dbox_setfield(d_ob, laif, numstring);
  3240.   dbox_setnumeric(d_ob, ladf, data.lat10%10);
  3241.  
  3242. /* Set N or S for Latitude.  */
  3243.   dbox_setfield(d_ob, nsf, (data.isn? "N" : "S"));
  3244.  
  3245. /* Set Longitude field (integer part, then decimal).    */
  3246.   sprintf(numstring, "%03.3i", data.long10/10);
  3247.   dbox_setfield(d_ob, loif, numstring);
  3248.   dbox_setnumeric(d_ob, lodf, data.long10%10);
  3249.  
  3250. /* Set E or W for Longitude. */
  3251.   dbox_setfield(d_ob, ewf, (data.isw? "W" : "E"));
  3252.  
  3253. /* Set Day field.                                       */
  3254.   sprintf(numstring, "%02.2i", data.day);
  3255.   dbox_setfield(d_ob, dayf, numstring);
  3256.  
  3257. /* Set Month field.                                     */
  3258.   sprintf(numstring, "%02.2i", data.month);
  3259.   dbox_setfield(d_ob, monf, numstring);
  3260.  
  3261. /* Set Year field.                                      */
  3262.   dbox_setnumeric(d_ob, yrf, data.year);
  3263.  
  3264. /* Set Hour field.                                      */
  3265.   sprintf(numstring, "%02.2i", data.hour);
  3266.   dbox_setfield(d_ob, hrf, numstring);
  3267.  
  3268. /* Set Minute field.                                    */
  3269.   sprintf(numstring, "%02.2i", data.min);
  3270.   dbox_setfield(d_ob, minf, numstring);
  3271.  
  3272. /* Set Offset field.                                    */
  3273.   sprintf(numstring, "%+05.1f", (double)data.offset);
  3274.   dbox_setfield(d_ob, offf, numstring);
  3275.  
  3276. /* Set 'direction' radio buttons.                       */
  3277.   for (i=0; i<NDIR; i++)
  3278.     dbox_setnumeric(d_ob, DIR_BASE+i, (i==data.dir? 1 : 0));
  3279.  
  3280.   return;
  3281. }
  3282.  
  3283.  
  3284. /*------------------------------------------------------*/
  3285. /*      Fn to read data from Observer Details box       */
  3286. /*------------------------------------------------------*/
  3287. static void data_from_box(observerstr *ptr)
  3288. {
  3289.   int i;
  3290.   int d;
  3291.   char buf[10];
  3292.  
  3293. /* Read latitude*10.  Combine integer and decimal parts.*/
  3294.   i = dbox_getnumeric(d_ob, laif);
  3295.   d = dbox_getnumeric(d_ob, ladf);
  3296.   ptr->lat10 = 10*i + d;
  3297.  
  3298. /* Read N or S for latitude.                            */
  3299.   dbox_getfield(d_ob, nsf, buf, 10);
  3300.   ptr->isn = (buf[0]=='N');
  3301.  
  3302. /* Construct latitude in radians.                       */
  3303.   ptr->latit = CONV*(REAL)ptr->lat10/(REAL)10;
  3304.   if (!ptr->isn)
  3305.     ptr->latit = -ptr->latit;
  3306.  
  3307. /* Read longitude*10. Combine integer and decimal parts.*/
  3308.   i = dbox_getnumeric(d_ob, loif);
  3309.   d = dbox_getnumeric(d_ob, lodf);
  3310.   ptr->long10 = 10*i + d;
  3311.  
  3312. /* Read E or W for longitude.                           */
  3313.   dbox_getfield(d_ob, ewf, buf, 10);
  3314.   ptr->isw = (buf[0]=='W');
  3315.  
  3316. /* Construct longitude in radians.                      */
  3317.   ptr->longit = CONV*(REAL)ptr->long10/(REAL)10;
  3318.   if (!ptr->isw)
  3319.     ptr->longit = -ptr->longit;
  3320.  
  3321. /* Read day.                                            */
  3322.   ptr->day   = dbox_getnumeric(d_ob, dayf);
  3323.  
  3324. /* Read month.                                          */
  3325.   ptr->month = dbox_getnumeric(d_ob, monf);
  3326.  
  3327. /* Read year.                                           */
  3328.   ptr->year  = dbox_getnumeric(d_ob, yrf);
  3329.  
  3330. /* Read hour.                                           */
  3331.   ptr->hour  = dbox_getnumeric(d_ob, hrf);
  3332.  
  3333. /* Read minute.                                         */
  3334.   ptr->min   = dbox_getnumeric(d_ob, minf);
  3335.  
  3336. /* Read time offset.                                    */
  3337.   dbox_getfield(d_ob, offf, buf, 10);
  3338.   sscanf(buf, "%f", &ptr->offset);
  3339.  
  3340. /* Read direction.                                      */
  3341. /* Search the 'direction' radio buttons for the one     */
  3342. /* that is ON.                                          */
  3343.   for (i=NDIR-1;\
  3344.        i>0 && dbox_getnumeric(d_ob, DIR_BASE+i)==0;\
  3345.        i--)
  3346.     ;
  3347. /* Set dir field.                                       */
  3348.   ptr->dir = i;
  3349.  
  3350. /* Fill in derived fields of observer data.             */
  3351.   fillin_obfields(ptr);
  3352.  
  3353.   return;
  3354. }
  3355.  
  3356. /*------------------------------------------------------*/
  3357. /*     Function to fill in the derived fields of the    */
  3358. /*             observerstr pointed to by ptr.           */
  3359. /*------------------------------------------------------*/
  3360. static void fillin_obfields(observerstr *ptr)
  3361. {
  3362.  
  3363. /* Set ang field to angle corresponding to dir field.   */
  3364.   ptr->ang = direction[ptr->dir];
  3365.  
  3366. /* Calculate days since 12:00 on 00/01/1900             */
  3367.   ptr->jul = datime_julian(ptr, ptr->hour, ptr->min);
  3368.  
  3369. /* Set local siderial time.                             */
  3370.   ptr->sid = datime_siderial(ptr, ptr->jul, ptr->hour, ptr->min);
  3371.  
  3372.   return;
  3373. }
  3374.  
  3375. /*------------------------------------------------------*/
  3376. /*        Function to save Observer Data to disc        */
  3377. /*------------------------------------------------------*/
  3378. static BOOL save_ob_data(void)
  3379. {
  3380.   observerstr new_data[1];  /* For data read from dbox. */
  3381.   FILE *defaults;           /* File handle.             */
  3382.   size_t numwritten;
  3383.  
  3384. /* Open disc file for writing.  Quit if error.          */
  3385.   defaults = fopen(data_file, "wb");
  3386.   if (defaults == NULL) return FALSE;
  3387.  
  3388. /* Read new data from dbox.                             */
  3389.   data_from_box(new_data);
  3390.  
  3391. /* Write Observer Details.      */
  3392.   numwritten = fwrite((void *)new_data, sizeof(observerstr), 1, defaults);
  3393.  
  3394. /* Close file.  Fatal error if it won't close.          */
  3395.   if (fclose(defaults) != 0)
  3396.     werr(FATAL, "SkyView Error: Can't close Default file");
  3397.  
  3398. /* Return success or failure.                           */
  3399.   return (numwritten == 1);
  3400. }
  3401.  
  3402. /*------------------------------------------------------*/
  3403. /*       Function to load Observer Data from disc       */
  3404. /*------------------------------------------------------*/
  3405. static BOOL load_ob_data(void)
  3406. {
  3407.   observerstr new_data[1]; /* For data read from disc.  */
  3408.   FILE *defaults;          /* File handle.              */
  3409.   size_t numread;
  3410.  
  3411. /* Open disc file for reading.  Quit if error.          */
  3412.   defaults = fopen(data_file, "rb");
  3413.   if (defaults == NULL) return FALSE;
  3414.  
  3415. /* Attempt to read Observer Details.                    */
  3416.   numread = fread((void *)new_data, sizeof(observerstr), 1, defaults);
  3417.  
  3418. /* Close disc file.  Fatal error if it won't close.     */
  3419.   if (fclose(defaults) != 0)
  3420.     werr(FATAL, "SkyView Error: Can't close Default file");
  3421.  
  3422. /* If read failed, do not load dbox.                    */
  3423.   if (numread != 1) return FALSE;
  3424.  
  3425. /* All OK. Load data into dbox & return TRUE.           */
  3426.   data_to_box(new_data[0]);
  3427.   return TRUE;
  3428. }
  3429.  
  3430.  
  3431. /*------------------------------------------------------*/
  3432. /*        Function to load System Date into dbox.       */
  3433. /*------------------------------------------------------*/
  3434. static void load_sys_date(void)
  3435. {
  3436.   time_t encoded_time;
  3437.   struct tm *sys_time;
  3438.   char numstring[10];  /* For No.-to-string conversions */
  3439.  
  3440. /* Read system time.                                    */
  3441.   encoded_time = time(NULL);
  3442.   if (encoded_time == (time_t)-1)
  3443.     return;
  3444.   sys_time = localtime(&encoded_time);
  3445.  
  3446. /* Set Day field.                                       */
  3447.   sprintf(numstring, "%02.2i", sys_time->tm_mday);
  3448.   dbox_setfield(d_ob, dayf, numstring);
  3449.  
  3450. /* Set Month field.                                     */
  3451.   sprintf(numstring, "%02.2i", 1 + sys_time->tm_mon);
  3452.   dbox_setfield(d_ob, monf, numstring);
  3453.  
  3454. /* Set Year field.                                      */
  3455.   dbox_setnumeric(d_ob, yrf, 1900 + sys_time->tm_year);
  3456.  
  3457.   return;
  3458. }
  3459.  
  3460.  
  3461. /*------------------------------------------------------*/
  3462. /*          Raw Event Handler for Observer Box          */
  3463. /*------------------------------------------------------*/
  3464. static BOOL ob_raw_handler(dbox d, void *event, void *handle)
  3465.  
  3466. /* Prevent a click with Adjust from clearing ALL the    */
  3467. /* 'direction' radio buttons at the same time.          */
  3468.  
  3469. {
  3470.   wimp_eventstr *e=(wimp_eventstr *)event;
  3471.   wimp_i icon;
  3472.  
  3473.   if (e->e == wimp_EBUT &&\
  3474.       e->data.but.m.bbits == wimp_BRIGHT)
  3475.   {
  3476.     icon = e->data.but.m.i;
  3477.     if (icon>=1 &&\
  3478.         icon<=8 &&\
  3479.         dbox_getnumeric(d,icon)==0)
  3480.     {
  3481.       dbox_setnumeric(d,icon,TRUE);
  3482.       return TRUE;
  3483.     }
  3484.   }
  3485.   return FALSE;
  3486. }
  3487.  
  3488.  
  3489. /********************************************************/
  3490. /*         Handling of Select on Icon Bar Icon          */
  3491. /********************************************************/
  3492.  
  3493. static void sv_iconclick(wimp_i icon)
  3494. {
  3495.   return;
  3496. }
  3497.  
  3498.  
  3499. /********************************************************/
  3500. /*     Function to add new object to plotting list.     */
  3501. /********************************************************/
  3502.  
  3503. void addobj(plotobj new_object, int *horiz_id, int *vert_id)
  3504. {
  3505.  
  3506.  
  3507. /* Add new object to Horiz window if it is above the    */
  3508. /* horizon but below alt_max, and if there is room in   */
  3509. /* the plotting list.                                   */
  3510.  
  3511.   if (horiz_data.number <  MAXOBJS   && \
  3512.          new_object.alt >  (REAL)0.0 && \
  3513.          new_object.alt <= alt_max)
  3514.   {
  3515.     /* Fill in the remaining info                       */
  3516.     /*(ie screen coords, bounding box, pointer to next).*/
  3517.     new_object.wind_x = x_h(new_object.azim);
  3518.     new_object.wind_y = y_h(new_object.alt);
  3519.     coords_offsetbox(&new_object.size, \
  3520.                       new_object.wind_x, \
  3521.                       new_object.wind_y, \
  3522.                      &new_object.bounds);
  3523.     new_object.next = 0;
  3524.  
  3525.     /* Add new object to list, and return an id number  */
  3526.     /* to the calling module.                           */
  3527.     horiz_list[horiz_data.number] = new_object;
  3528.     *horiz_id = horiz_data.number++;
  3529.   }
  3530.   else
  3531.     /* Inform calling module that obj is not in window. */
  3532.     *horiz_id = NOWHERE;
  3533.  
  3534.  
  3535. /* Add new object to Vert window if it is above         */
  3536. /* alt_min, and if there is room in the plotting list.  */
  3537.  
  3538.   if (vert_data.number <  MAXOBJS   && \
  3539.          new_object.alt >= alt_min)
  3540.   {
  3541.     /* Fill in the remaining info                       */
  3542.     /*(ie screen coords, bounding box, pointer to next).*/
  3543.     xy_v( new_object.alt,     new_object.azim, \
  3544.          &new_object.wind_x, &new_object.wind_y);
  3545.     coords_offsetbox(&new_object.size, \
  3546.                       new_object.wind_x, \
  3547.                       new_object.wind_y, \
  3548.                      &new_object.bounds);
  3549.     new_object.next = 0;
  3550.  
  3551.     /* Add new object to list, and return an id number  */
  3552.     /* to the calling module.                           */
  3553.     vert_list[vert_data.number] = new_object;
  3554.     *vert_id = vert_data.number++;
  3555.   }
  3556.   else
  3557.     /* Inform calling module that obj is not in window. */
  3558.     *vert_id = NOWHERE;
  3559.  
  3560.  
  3561.   return;
  3562. }
  3563.  
  3564.  
  3565. /********************************************************/
  3566. /*    Functions to re-calculate screen x and y values   */
  3567. /*         following change of direction of view        */
  3568. /********************************************************/
  3569. /*------------------------------------------------------*/
  3570. /*                   Horiz Window xy                    */
  3571. /*------------------------------------------------------*/
  3572. static void recalc_horiz_xy(void)
  3573. {
  3574.   plotobj *p;
  3575.   /* Point to end of Horiz list:                        */
  3576.   plotobj *pend = horiz_list + horiz_data.number;
  3577.  
  3578.   for(p = horiz_list; p < pend; p++)
  3579.   {
  3580.   /* Fill in x, y, bounding box and pointer to next.    */
  3581.     p->wind_x = x_h(p->azim);
  3582.     p->wind_y = y_h(p->alt);
  3583.     coords_offsetbox(&p->size, p->wind_x, p->wind_y, &p->bounds);
  3584.     p->next = 0;
  3585.   }
  3586.  
  3587.   return;
  3588. }
  3589.  
  3590. /*------------------------------------------------------*/
  3591. /*                    Vert Window xy                    */
  3592. /*------------------------------------------------------*/
  3593. static void recalc_vert_xy(void)
  3594. {
  3595.   plotobj *p;
  3596.   /* Point to end of Vert list:                         */
  3597.   plotobj *pend = vert_list + vert_data.number;
  3598.  
  3599.   for (p = vert_list; p < pend; p++)
  3600.   {
  3601.   /* Fill in x, y, bounding box and pointer to next.    */
  3602.     xy_v(p->alt, p->azim, &p->wind_x, &p->wind_y);
  3603.     coords_offsetbox(&p->size, p->wind_x, p->wind_y, &p->bounds);
  3604.     p->next = 0;
  3605.   }
  3606.  
  3607.   return;
  3608. }
  3609.  
  3610. /********************************************************/
  3611. /*          Function to register a module.              */
  3612. /********************************************************/
  3613. BOOL register_module(initfntype *initfn)
  3614. {
  3615.   BOOL text_null;        /* TRUE for null menu entries. */
  3616.   modulestr new_module;  /* For info on new module.     */
  3617.  
  3618. /*------------------------------------------------------*/
  3619. /*      Check there is space to register module.        */
  3620. /*------------------------------------------------------*/
  3621.   if (module_count >= MAXMODULES) return FALSE;
  3622.  
  3623. /*------------------------------------------------------*/
  3624. /*                  Obtain Module Info                  */
  3625. /*------------------------------------------------------*/
  3626.   /* Call initialisation function and obtain info on    */
  3627.   /* new module.  Abandon if initialisation fails.      */
  3628.   if (!initfn(module_count, &new_module)) return FALSE;
  3629.  
  3630. /*------------------------------------------------------*/
  3631. /*    Record whether module is initially enabled or     */
  3632. /*                  initially disabled.                 */
  3633. /*------------------------------------------------------*/
  3634.   enabled[module_count]=new_module.initial;
  3635.  
  3636. /*------------------------------------------------------*/
  3637. /*           Record new List-Building function          */
  3638. /*------------------------------------------------------*/
  3639.   module_fns[module_count].buildfn = new_module.buildfn;
  3640.  
  3641. /*------------------------------------------------------*/
  3642. /*               Record new Info Function               */
  3643. /*------------------------------------------------------*/
  3644.   module_fns[module_count].infofn = new_module.infofn;
  3645.  
  3646. /*------------------------------------------------------*/
  3647. /*             Deal with Select function.               */
  3648. /*------------------------------------------------------*/
  3649.   /* Check for illegal combinations of NULL items.      */
  3650.  
  3651.   text_null = (*new_module.select_entry == '\0');
  3652.   /* Quit if select fn is NULL but text entry isn't.    */
  3653.   if (new_module.selectfn  == NULL  && !text_null) 
  3654.     return FALSE;
  3655.   /* Quit if text entry is NULL but select menu isn't.  */
  3656.   if (text_null && new_module.select_menu  != NULL)
  3657.     return FALSE;
  3658.  
  3659.   /* Checks passed OK, so record Select function for    */
  3660.   /* this module.                                       */
  3661.   module_fns[module_count].selectfn=new_module.selectfn;
  3662.  
  3663.   /* Install menu entry (if any) and sub-menu (if any). */
  3664.   if (!text_null)
  3665.   {
  3666.     if (!install_select_entry(new_module)) return FALSE;
  3667.     /* Set module_index to record that this module owns */
  3668.     /* the newly-installed entry in the Select menu.    */
  3669.     module_index[module_count] = select_count;
  3670.     /* Ie, module m owns menu entry module_index[m]     */
  3671.   }
  3672.   else
  3673.     /* Set module_index to record that this module does */
  3674.     /* not own an entry in the Select menu.             */
  3675.     module_index[module_count] = NO_ENTRY;
  3676.  
  3677. /*------------------------------------------------------*/
  3678. /*             Deal with Display function.              */
  3679. /*------------------------------------------------------*/
  3680.   /* Check for illegal combinations of NULL items.      */
  3681.  
  3682.   text_null = (*new_module.display_entry == '\0');
  3683.   /* Quit if display fn is NULL but text entry isn't.   */
  3684.   if (new_module.dispfn == NULL  && !text_null)
  3685.     return FALSE;
  3686.   /* Quit if text entry is NULL but display menu isn't. */
  3687.   if (text_null && new_module.display_menu  != NULL)
  3688.     return FALSE;
  3689.  
  3690.   /* Checks passed OK, so record Display function for   */
  3691.   /* this module.                                       */
  3692.   module_fns[module_count].dispfn = new_module.dispfn;
  3693.  
  3694.   /* Install menu entry (if any) and sub-menu (if any). */
  3695.   if (!text_null)
  3696.     if (!install_display_entry(new_module)) return FALSE;
  3697.  
  3698. /*------------------------------------------------------*/
  3699. /*                 Module installed OK                  */
  3700. /*------------------------------------------------------*/
  3701.   /* Increment module count to show the no. of modules. */
  3702.   module_count++;
  3703.  
  3704.   return TRUE;
  3705. }
  3706.  
  3707.  
  3708. /*------------------------------------------------------*/
  3709. /*       Install Select menu entry for a Module.        */
  3710. /*------------------------------------------------------*/
  3711. static BOOL install_select_entry(modulestr new_module)
  3712. {
  3713.  
  3714.   /* Add text entry to menu.  Use menu_new the first    */
  3715.   /* time, and menu_extend thereafter.                  */
  3716.   if (select_count == 0)
  3717.   {
  3718.     sel_sub_menu = menu_new(SEL_SUB_NAME, new_module.select_entry);
  3719.     if (sel_sub_menu == NULL) return FALSE;
  3720.   }
  3721.   else
  3722.     menu_extend(sel_sub_menu, new_module.select_entry);
  3723.  
  3724.   /* Update sel_index array to associate new module     */
  3725.   /* with this entry in the Select menu.                */
  3726.   /* ie Entry n is owned by module sel_index[n]         */
  3727.   sel_index[++select_count] = module_count;
  3728.  
  3729.   /* Set tick/fade status of menu entry, according to   */
  3730.   /* whether module is enabled or disabled.             */
  3731.   menu_setflags(sel_sub_menu, select_count, 0, !enabled[module_count]);
  3732.  
  3733.   /* Attach sub-menu, if one was provided.              */
  3734.   if (new_module.select_menu != NULL)
  3735.     menu_submenu(sel_sub_menu, select_count, new_module.select_menu);
  3736.  
  3737.   return TRUE;
  3738. }
  3739.  
  3740. /*------------------------------------------------------*/
  3741. /*        Install Display menu entry for a Module.      */
  3742. /*------------------------------------------------------*/
  3743. static BOOL install_display_entry(modulestr new_module)
  3744. {
  3745.  
  3746.   /* Add text entry to menu.  Use menu_new the first    */
  3747.   /* time, and menu_extend thereafter.                  */
  3748.   if (display_count == 0)
  3749.   {
  3750.     display_sub_menu = menu_new(DISP_SUB_NAME, new_module.display_entry);
  3751.     if (display_sub_menu == NULL) return FALSE;
  3752.   }
  3753.   else
  3754.     menu_extend(display_sub_menu, new_module.display_entry);
  3755.  
  3756.   /* Update index array to associate new module with    */
  3757.   /* this entry in the Display menu.                    */
  3758.   disp_index[++display_count] = module_count;
  3759.  
  3760.   /* Set ticked/faded status of menu entry, according   */
  3761.   /* to whether module is enabled or disabled.          */
  3762.   menu_setflags(display_sub_menu, display_count,\
  3763.                 enabled[module_count], 0);
  3764.  
  3765.   /* Attach sub-menu, if one was provided.              */
  3766.   if (new_module.display_menu != NULL)
  3767.     menu_submenu(display_sub_menu, display_count, new_module.display_menu);
  3768.  
  3769.   return TRUE;
  3770. }
  3771.  
  3772.   
  3773.  
  3774. /********************************************************/
  3775. /*                    INITIALISATION                    */
  3776. /********************************************************/
  3777.  
  3778. static BOOL sv_initialise(void)
  3779. {
  3780.   REAL big_r;   /* Max dist from origin of Vert window  */
  3781.                 /* to any point in that window.         */
  3782.   int compass_rad;   /* Radius of Vert compass circle.  */
  3783.   int i;        /* General purpose counter.             */
  3784.   REAL window_factor;/* Plotting scale calculated from  */
  3785.                      /* size of Horiz window.           */
  3786.  
  3787. /*------------------------------------------------------*/
  3788. /*        Read SkyView$Dir and form file name           */
  3789. /*------------------------------------------------------*/
  3790.   os_read_var_val("SkyView$Dir", app_dir, 245);
  3791.   if (*app_dir == '\0') return FALSE;
  3792.  
  3793. /* Copy SkyView$Dir into array for file name.           */
  3794.   strcpy(data_file, app_dir);
  3795.  
  3796. /* Append file name */
  3797.   strcat(data_file, "." DEF_NAME);
  3798.  
  3799. /*------------------------------------------------------*/
  3800. /*                  Creation of menus                   */
  3801. /*------------------------------------------------------*/
  3802. /* Main menu structure.                                 */
  3803.   if (main_menu=menu_new(MAIN_NAME, MAIN_MENU_ITEMS), \
  3804.       main_menu==NULL)
  3805.     return FALSE;
  3806.  
  3807. /* Sub menu structure for 'View sel.'.                  */
  3808.   if (view_sub_menu=menu_new(VIEW_SUB_NAME, VIEW_SUB_MENU_ITEMS), \
  3809.       view_sub_menu==NULL)
  3810.     return FALSE;
  3811.  
  3812. /* Attach 'View sel.' sub-menu.                         */
  3813.   menu_submenu(main_menu, item_view, view_sub_menu);
  3814.  
  3815. /* Sub menu structure for 'Print'.                      */
  3816.   print_sub_menu = menu_new(PRINT_SUB_NAME, PRINT_SUB_MENU_ITEMS);
  3817.   if (print_sub_menu == NULL) return FALSE;
  3818.  
  3819. /* Attach 'Print' sub-menu.                             */
  3820.   menu_submenu(main_menu, item_print, print_sub_menu);
  3821.  
  3822. /*------------------------------------------------------*/
  3823. /*                  Horizontal Window                   */
  3824. /*------------------------------------------------------*/
  3825. /* Create Horiz window         */
  3826.   if (!create_window(HORIZ_NAME, &horiz_handle, &horiz_size))
  3827.     return FALSE;
  3828.  
  3829. /*    Initialise data structure for Horiz window       */
  3830.   horiz_data.open = FALSE;                         /* Open/Closed flag     */
  3831.   horiz_data.compass_drawfn = draw_horiz_compass;  /* Compass-drawing fn   */
  3832.   horiz_data.listptr = horiz_list;                 /* Point to plot list   */
  3833.   horiz_data.number = 0;                           /* No objs. in list.    */
  3834.   horiz_data.selnum = NOWHERE;                     /* No selected obj.     */
  3835.   horiz_data.matrix.xx = 0;                        /* Transformation matrix*/
  3836.   horiz_data.matrix.xy =  SCALFACT;                /* for printing.  Scale */
  3837.   horiz_data.matrix.yx = -SCALFACT;                /* by SCALFACT and      */
  3838.   horiz_data.matrix.yy = 0;                        /* rotate by 90°.       */
  3839.   horiz_data.posn.dx = 540000;                     /* Position of print    */
  3840.   horiz_data.posn.dy = 108000;                     /* rect. on page.       */
  3841.   horiz_data.title_buildfn = build_horiz_title;    /* Title-building fn.   */
  3842.   horiz_data.title_writefn = write_horiz_title;    /* Title-writing  fn.   */
  3843.  
  3844. /* Register event handler for Horiz window */
  3845.   win_register_event_handler(horiz_handle, sv_win_event_handler, &horiz_data);
  3846.  
  3847. /* Establish Menu Handler and Pre-menu Proc for Horiz. */
  3848.   if(!event_attachmenumaker(horiz_handle,main_premenuproc,main_menuproc,0))
  3849.     return FALSE;
  3850.  
  3851. /* Establish screen scaling factor from                 */
  3852. /* width of Horiz window.                               */
  3853.   width = (horiz_size.x1 - horiz_size.x0);
  3854.   window_factor  = (REAL)width/((REAL)2.0*PI);
  3855.  
  3856. /* Establish max altitude in Horiz window               */
  3857.   height = horiz_size.y1;
  3858.   alt_max= (REAL)height/window_factor;
  3859.  
  3860. /*------------------------------------------------------*/
  3861. /*                     Vert Window                      */
  3862. /*------------------------------------------------------*/
  3863. /* Create Vert window         */
  3864.   if (!create_window(VERT_NAME, &vert_handle, &vert_size))
  3865.     return FALSE;
  3866.  
  3867. /*     Initialise data structure for Vert window.       */
  3868.   vert_data.open = FALSE;                         /* Open/Closed flag     */
  3869.   vert_data.compass_drawfn = draw_vert_compass;   /* Compass-drawing fn.  */
  3870.   vert_data.listptr = vert_list;                  /* Point to plot list.  */
  3871.   vert_data.number = 0;                           /* No objs. in list.    */
  3872.   vert_data.selnum = NOWHERE;                     /* No selected obj.     */
  3873.   vert_data.matrix.xx = SCALFACT;                 /* Transformation matrix*/
  3874.   vert_data.matrix.xy = 0;                        /* for printing.  Scale */
  3875.   vert_data.matrix.yx = 0;                        /* by SCALFACT and      */
  3876.   vert_data.matrix.yy = SCALFACT;                 /* rotate by 0°.        */
  3877.   vert_data.posn.dx =  80000;                     /* Position of print    */
  3878.   vert_data.posn.dy = 144000;                     /* rect. on page.       */
  3879.   vert_data.title_buildfn = build_vert_title;     /* Title-building fn.   */
  3880.   vert_data.title_writefn = write_vert_title;     /* Title-writing  fn.   */
  3881.  
  3882. /* Register event handler for Vert window */
  3883.   win_register_event_handler(vert_handle, sv_win_event_handler, &vert_data);
  3884.  
  3885. /* Establish Menu Handler and Pre-menu Proc for Vert.   */
  3886.   if(!event_attachmenumaker(vert_handle,main_premenuproc,main_menuproc,0))
  3887.     return FALSE;
  3888.  
  3889. /* Establish min altitude in Vert window.              */
  3890. /* First get maximum distance from origin to any point */
  3891. /* in Vert window.                                     */
  3892.   {
  3893.     int corner1, corner2, corner3, corner4;
  3894.     int r1, r2;
  3895.     int r_max;
  3896.     corner1 = vert_size.x0 * vert_size.x0 +
  3897.               vert_size.y0 * vert_size.y0;
  3898.     corner2 = vert_size.x0 * vert_size.x0 +
  3899.               vert_size.y1 * vert_size.y1;
  3900.     corner3 = vert_size.x1 * vert_size.x1 +
  3901.               vert_size.y0 * vert_size.y0;
  3902.     corner4 = vert_size.x1 * vert_size.x1 +
  3903.               vert_size.y1 * vert_size.y1;
  3904.     r1 = (corner1 > corner2 ? corner1 : corner2);
  3905.     r2 = (corner3 > corner4 ? corner3 : corner4);
  3906.     r_max = (r1 > r2 ? r1 : r2);
  3907.     big_r = (REAL)sqrt((double)r_max);
  3908.   }
  3909. /* Can now calculate min altitude.                      */
  3910.   alt_min = PIby2 - big_r/window_factor;
  3911.   if (alt_min < (REAL)0.0)
  3912.     alt_min = (REAL)0.0;
  3913.  
  3914. /*     Construct a Draw Path consisting of a circle     */
  3915. /*                 and NDIR tick marks.                 */
  3916.  
  3917. /* Set plotting factor to suit units used by Draw       */
  3918. /* functions.                                           */
  3919.   factor = (REAL)DrawU * window_factor;
  3920.  
  3921. /* Calculate radius of compass circle.                  */
  3922.   compass_rad = (int)(factor*(PIby2-compass_alt));
  3923.  
  3924. /* Set up Path describing circle.                       */
  3925.   sv_Draw_circle(compass_rad);
  3926.  
  3927. /* Set up Path describing NDIR tick marks.              */
  3928.   for (i=0; i<NDIR; i++)
  3929.   {
  3930.     int x, y;
  3931.     xy_v(compass_alt, direction[i], &x, &y);
  3932.     sv_Draw_move(x,y);
  3933.     xy_v(compass_alt*(REAL)0.98, direction[i], &x, &y);
  3934.     sv_Draw_line(x,y);
  3935.   }
  3936.  
  3937. /* Write end-of-path marker.                            */
  3938.   sv_Draw_endp();
  3939.  
  3940. /* Process the Draw Path into a semi-digested form which*/
  3941. /* will be quicker to plot  (at the penalty of being    */
  3942. /* unable to take advantage of any increase in screen   */
  3943. /* resolution due to a later mode change).              */
  3944.   draw_regsP.r[0] = (int)path;     /* Input path.       */
  3945.   draw_regsP.r[1] = (int)semi;     /* Output path.      */
  3946.   draw_regsP.r[2] = (int)&matrix;  /* Transformation.   */
  3947.   draw_regsP.r[3] = 0;             /* Default flatness. */
  3948.   draw_regsP.r[4] = 4*DrawU;       /* 4 OS units thick. */
  3949.   draw_regsP.r[5] = (int)&cj_style;/* Caps & joins.     */
  3950.   draw_regsP.r[6] = 0;             /* No dashes.        */
  3951.   wimpt_noerr(os_swix(sv_XDraw_StrokePath, &draw_regsP));
  3952.  
  3953. /* Set up register set for later call to plot the semi- */
  3954. /* digested Path.                                       */
  3955.   draw_regsS.r[0] = (int)semi;    /* Input path.        */
  3956.   draw_regsS.r[1] = 0x30;         /* Fill only. Style.  */
  3957.   draw_regsS.r[2] = 0;            /* Transform 1:1      */
  3958.   draw_regsS.r[3] = 0;            /* Flatness not used. */
  3959.   draw_regsS.r[4] = 0;            /* Thickness not used.*/
  3960.   draw_regsS.r[5] = 0;            /* Caps joins not used*/
  3961.   draw_regsS.r[6] = 0;            /* No dashes.         */
  3962.   draw_regsS.r[7] = 2;            /* Fill by subpaths.  */
  3963.  
  3964. /* Set up register set for later call to print 'raw'    */
  3965. /* Draw Path (to take full advantage of printer resoln.)*/
  3966.   draw_regsP.r[1] = 0x30;         /* Fill only. Style.  */
  3967.  
  3968. /* Set plotting factor to suit Horiz and Vert windows.  */
  3969.   factor = window_factor;
  3970.  
  3971. /*------------------------------------------------------*/
  3972. /*    Register current screen mode, and set sprite -    */
  3973. /*                   plotting tables.                   */
  3974. /*------------------------------------------------------*/
  3975.   wimpt_checkmode();
  3976.   wimpt_noerr(sprite_tables());
  3977.  
  3978. /*------------------------------------------------------*/
  3979. /*               dbox for Observer Details              */
  3980. /*------------------------------------------------------*/
  3981.  
  3982. /* Create dbox                 */
  3983.   if (d_ob = dbox_new(OB_NAME), d_ob == NULL)
  3984.     return FALSE;
  3985.  
  3986. /* Establish raw event handler */
  3987.   dbox_raw_eventhandler(d_ob, ob_raw_handler, 0);
  3988.  
  3989. /* Find out window handle      */
  3990.   ob_handle = (wimp_w)dbox_syshandle(d_ob);
  3991.  
  3992. /* Attach menu & pre-menu proc */
  3993.   if(!event_attachmenumaker(ob_handle,main_premenuproc,main_menuproc,0))
  3994.     return FALSE;
  3995.  
  3996. /* Set dbox fields to Factory Default Settings.         */
  3997.   data_to_box(ob_data);
  3998.  
  3999. /* Attempt to load user's default settings into dbox    */
  4000. /* from disc.                                           */
  4001.   load_ob_data();
  4002.  
  4003. /* Read observer details back from dbox.  These will be */
  4004. /* user's defaults if attempt to read was sucessful, and*/
  4005. /* factory defaults otherwise.                          */
  4006.   data_from_box(&ob_data);
  4007.  
  4008. /*------------------------------------------------------*/
  4009. /*                       Info Box                       */
  4010. /*------------------------------------------------------*/
  4011.  
  4012. /* Create dbox.                                         */
  4013.   if (d_info = dbox_new(INFO_NAME), d_info == NULL)
  4014.     return FALSE;
  4015.  
  4016. /*------------------------------------------------------*/
  4017. /*                    Icon Bar Icon                     */
  4018. /*------------------------------------------------------*/
  4019. /* Put icon on icon bar        */
  4020. /* and register icon click handler */
  4021.   baricon(ICON_NAME, 1, sv_iconclick);
  4022.  
  4023. /* Establish icon menu handler */
  4024. /* and menu pre-proc function  */
  4025.   if(!event_attachmenumaker(win_ICONBAR,main_premenuproc,main_menuproc,0))
  4026.     return FALSE;
  4027.  
  4028. /*------------------------------------------------------*/
  4029. /*               Registration of Modules                */
  4030. /*------------------------------------------------------*/
  4031.  
  4032.   if(!register_module(sun_initfn)) return FALSE;
  4033.   if(!register_module(moon_initfn)) return FALSE;
  4034.   if(!register_module(plans_initfn)) return FALSE;
  4035.   if(!register_module(star_initfn)) return FALSE;
  4036.   if(!register_module(markers_initfn)) return FALSE;
  4037.   if(!register_module(tvsats_initfn)) return FALSE;
  4038.   if(!register_module(userobj_initfn)) return FALSE;
  4039.  
  4040. /* If there are any items in the Display sub-menu,      */
  4041. /* attach the sub-menu and unfade the Display item.     */
  4042.   if (display_count > 0)
  4043.   {
  4044.     menu_submenu(main_menu, item_display, display_sub_menu);
  4045.     menu_setflags(main_menu, item_display, 0, 0);
  4046.   }
  4047.  
  4048. /* If there are any items in the Select sub-menu,       */
  4049. /* attach the sub-menu.  Unfading is handled by the     */
  4050. /* menumaker, as it depends on whether the user has     */
  4051. /* verified the observer details.                       */
  4052.   if (select_count > 0)
  4053.     menu_submenu(main_menu, item_select, sel_sub_menu);
  4054.  
  4055. /*------------------------------------------------------*/
  4056. /*                 End of Initialisation                */
  4057. /*------------------------------------------------------*/
  4058.   return TRUE;
  4059. }
  4060.  
  4061. /********************************************************/
  4062. /*                 The MAIN function                    */
  4063. /********************************************************/
  4064.  
  4065. int main(int argc, char *argv[])
  4066. {
  4067.   BOOL init_ok;
  4068.  
  4069.   if (argc>1) strcpy(start_file, argv[1]);
  4070.  
  4071. /*             RISC_OSlib initialisation                */
  4072.   wimpt_init(APP_NAME);  /* initialise task */
  4073.   res_init(APP_NAME);    /*    resources    */
  4074.   resspr_init();         /*     sprites     */
  4075.   template_init();       /*    templates    */
  4076.   dbox_init();           /* dialogue boxes  */
  4077.   visdelay_init();       /*    hourglass    */
  4078.  
  4079. /*              Other initialisation                    */
  4080.   visdelay_begin();
  4081.   init_ok = sv_initialise();
  4082.   visdelay_end();
  4083.  
  4084.   if (init_ok)
  4085.   {
  4086.     /* The main event loop */
  4087.     while (TRUE)
  4088.       event_process();
  4089.   }
  4090.  
  4091.   return 0;
  4092. }
  4093.